var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/jacdac.ts var jacdac_exports = {}; __export(jacdac_exports, { ACK_MAX_DELAY: () => ACK_MAX_DELAY, ACK_MIN_DELAY: () => ACK_MIN_DELAY, ADVERTISE: () => ADVERTISE, ALIGN: () => ALIGN, ANNOUNCE: () => ANNOUNCE, AccelerometerEvent: () => AccelerometerEvent, AccelerometerReg: () => AccelerometerReg, AccelerometerRegPack: () => AccelerometerRegPack, AcidityReg: () => AcidityReg, AcidityRegPack: () => AcidityRegPack, AirPressureReg: () => AirPressureReg, AirPressureRegPack: () => AirPressureRegPack, AirQualityIndexReg: () => AirQualityIndexReg, AirQualityIndexRegPack: () => AirQualityIndexRegPack, AnalogSensorServer: () => AnalogSensorServer, ArcadeGamepadButton: () => ArcadeGamepadButton, ArcadeGamepadEvent: () => ArcadeGamepadEvent, ArcadeGamepadEventPack: () => ArcadeGamepadEventPack, ArcadeGamepadReg: () => ArcadeGamepadReg, ArcadeGamepadRegPack: () => ArcadeGamepadRegPack, ArcadeSoundCmd: () => ArcadeSoundCmd, ArcadeSoundCmdPack: () => ArcadeSoundCmdPack, ArcadeSoundReg: () => ArcadeSoundReg, ArcadeSoundRegPack: () => ArcadeSoundRegPack, BLUETOOTH_JACDAC_DIAG_CHARACTERISTIC: () => BLUETOOTH_JACDAC_DIAG_CHARACTERISTIC, BLUETOOTH_JACDAC_RX_CHARACTERISTIC: () => BLUETOOTH_JACDAC_RX_CHARACTERISTIC, BLUETOOTH_JACDAC_SERVICE: () => BLUETOOTH_JACDAC_SERVICE, BLUETOOTH_JACDAC_TX_CHARACTERISTIC: () => BLUETOOTH_JACDAC_TX_CHARACTERISTIC, BLUETOOTH_TRANSPORT: () => BLUETOOTH_TRANSPORT, BOUND: () => BOUND, BUS_NODE_NAME: () => BUS_NODE_NAME, BarcodeReaderEvent: () => BarcodeReaderEvent, BarcodeReaderEventPack: () => BarcodeReaderEventPack, BarcodeReaderFormat: () => BarcodeReaderFormat, BarcodeReaderReg: () => BarcodeReaderReg, BarcodeReaderRegPack: () => BarcodeReaderRegPack, BaseCmd: () => BaseCmd, BaseCmdPack: () => BaseCmdPack, BaseEvent: () => BaseEvent, BaseEventPack: () => BaseEventPack, BaseReg: () => BaseReg, BaseRegPack: () => BaseRegPack, BitRadioCmd: () => BitRadioCmd, BitRadioCmdPack: () => BitRadioCmdPack, BitRadioReg: () => BitRadioReg, BitRadioRegPack: () => BitRadioRegPack, BootloaderCmd: () => BootloaderCmd, BootloaderCmdPack: () => BootloaderCmdPack, BootloaderError: () => BootloaderError, BrailleDisplayReg: () => BrailleDisplayReg, BrailleDisplayRegPack: () => BrailleDisplayRegPack, BrailleDisplayServer: () => BrailleDisplayServer, BridgeReg: () => BridgeReg, BridgeRegPack: () => BridgeRegPack, BusInteractionMode: () => BusInteractionMode, ButtonEvent: () => ButtonEvent, ButtonEventPack: () => ButtonEventPack, ButtonReg: () => ButtonReg, ButtonRegPack: () => ButtonRegPack, ButtonServer: () => ButtonServer, BuzzerCmd: () => BuzzerCmd, BuzzerCmdPack: () => BuzzerCmdPack, BuzzerReg: () => BuzzerReg, BuzzerRegPack: () => BuzzerRegPack, BuzzerServer: () => BuzzerServer, CHANGE: () => CHANGE, CLOSE: () => CLOSE, CLOUD_COMMAND: () => CLOUD_COMMAND, CMD_ADVERTISEMENT_DATA: () => CMD_ADVERTISEMENT_DATA, CMD_EVENT_CODE_MASK: () => CMD_EVENT_CODE_MASK, CMD_EVENT_COUNTER_MASK: () => CMD_EVENT_COUNTER_MASK, CMD_EVENT_COUNTER_POS: () => CMD_EVENT_COUNTER_POS, CMD_EVENT_MASK: () => CMD_EVENT_MASK, CMD_GET_REG: () => CMD_GET_REG, CMD_REG_MASK: () => CMD_REG_MASK, CMD_SET_REG: () => CMD_SET_REG, CMD_TOP_MASK: () => CMD_TOP_MASK, CMSISProto: () => CMSISProto, COMMAND_NODE_NAME: () => COMMAND_NODE_NAME, COMMAND_RECEIVE: () => COMMAND_RECEIVE, CONNECT: () => CONNECT, CONNECTING: () => CONNECTING, CONNECTION_STATE: () => CONNECTION_STATE, CONST_LED_MAX_PIXELS_LENGTH: () => CONST_LED_MAX_PIXELS_LENGTH, CONST_NODE_NAME: () => CONST_NODE_NAME, CONST_SYSTEM_ANNOUNCE_INTERVAL: () => CONST_SYSTEM_ANNOUNCE_INTERVAL, CRC_ACK_NODE_NAME: () => CRC_ACK_NODE_NAME, CapacitiveButtonCmd: () => CapacitiveButtonCmd, CapacitiveButtonReg: () => CapacitiveButtonReg, CapacitiveButtonRegPack: () => CapacitiveButtonRegPack, CharacterScreenReg: () => CharacterScreenReg, CharacterScreenRegPack: () => CharacterScreenRegPack, CharacterScreenServer: () => CharacterScreenServer, CharacterScreenTextDirection: () => CharacterScreenTextDirection, CharacterScreenVariant: () => CharacterScreenVariant, CloudAdapterCmd: () => CloudAdapterCmd, CloudAdapterCmdPack: () => CloudAdapterCmdPack, CloudAdapterEvent: () => CloudAdapterEvent, CloudAdapterEventPack: () => CloudAdapterEventPack, CloudAdapterReg: () => CloudAdapterReg, CloudAdapterRegPack: () => CloudAdapterRegPack, CloudAdapterServer: () => CloudAdapterServer, CloudConfigurationCmd: () => CloudConfigurationCmd, CloudConfigurationCmdPack: () => CloudConfigurationCmdPack, CloudConfigurationConnectionStatus: () => CloudConfigurationConnectionStatus, CloudConfigurationEvent: () => CloudConfigurationEvent, CloudConfigurationEventPack: () => CloudConfigurationEventPack, CloudConfigurationReg: () => CloudConfigurationReg, CloudConfigurationRegPack: () => CloudConfigurationRegPack, CodalMessageBusCmd: () => CodalMessageBusCmd, CodalMessageBusCmdPack: () => CodalMessageBusCmdPack, CodalMessageBusEvent: () => CodalMessageBusEvent, CodalMessageBusEventPack: () => CodalMessageBusEventPack, ColorReg: () => ColorReg, ColorRegPack: () => ColorRegPack, CompassCmd: () => CompassCmd, CompassReg: () => CompassReg, CompassRegPack: () => CompassRegPack, CompassServer: () => CompassServer, ConnectionState: () => ConnectionState, ControlAnnounceFlags: () => ControlAnnounceFlags, ControlCmd: () => ControlCmd, ControlCmdPack: () => ControlCmdPack, ControlPipe: () => ControlPipe, ControlPipePack: () => ControlPipePack, ControlReg: () => ControlReg, ControlRegPack: () => ControlRegPack, ControlServer: () => ControlServer, DATA: () => DATA, DEVICE_ANNOUNCE: () => DEVICE_ANNOUNCE, DEVICE_CHANGE: () => DEVICE_CHANGE, DEVICE_CLEAN: () => DEVICE_CLEAN, DEVICE_CONNECT: () => DEVICE_CONNECT, DEVICE_DISCONNECT: () => DEVICE_DISCONNECT, DEVICE_FIRMWARE_INFO: () => DEVICE_FIRMWARE_INFO, DEVICE_FOUND: () => DEVICE_FOUND, DEVICE_IMAGE_HEIGHT: () => DEVICE_IMAGE_HEIGHT, DEVICE_IMAGE_WIDTH: () => DEVICE_IMAGE_WIDTH, DEVICE_LOST: () => DEVICE_LOST, DEVICE_NODE_NAME: () => DEVICE_NODE_NAME, DEVICE_PACKET_ANNOUNCE: () => DEVICE_PACKET_ANNOUNCE, DEVICE_RESTART: () => DEVICE_RESTART, DISCONNECT: () => DISCONNECT, DISCONNECTING: () => DISCONNECTING, DISPOSE: () => DISPOSE, DOCS_ROOT: () => DOCS_ROOT, DcCurrentMeasurementReg: () => DcCurrentMeasurementReg, DcCurrentMeasurementRegPack: () => DcCurrentMeasurementRegPack, DcVoltageMeasurementReg: () => DcVoltageMeasurementReg, DcVoltageMeasurementRegPack: () => DcVoltageMeasurementRegPack, DcVoltageMeasurementVoltageMeasurementType: () => DcVoltageMeasurementVoltageMeasurementType, DeviceCatalog: () => DeviceCatalog, DeviceScriptConditionCmd: () => DeviceScriptConditionCmd, DeviceScriptConditionEvent: () => DeviceScriptConditionEvent, DeviceScriptManagerClient: () => DeviceScriptManagerClient, DeviceScriptManagerCmd: () => DeviceScriptManagerCmd, DeviceScriptManagerCmdPack: () => DeviceScriptManagerCmdPack, DeviceScriptManagerEvent: () => DeviceScriptManagerEvent, DeviceScriptManagerEventPack: () => DeviceScriptManagerEventPack, DeviceScriptManagerPipe: () => DeviceScriptManagerPipe, DeviceScriptManagerPipePack: () => DeviceScriptManagerPipePack, DeviceScriptManagerReg: () => DeviceScriptManagerReg, DeviceScriptManagerRegPack: () => DeviceScriptManagerRegPack, DeviceScriptManagerServer: () => DeviceScriptManagerServer, DeviceStatsMonitor: () => DeviceStatsMonitor, DevsDbgCmd: () => DevsDbgCmd, DevsDbgCmdPack: () => DevsDbgCmdPack, DevsDbgEvent: () => DevsDbgEvent, DevsDbgEventPack: () => DevsDbgEventPack, DevsDbgFiberHandle: () => DevsDbgFiberHandle, DevsDbgFunIdx: () => DevsDbgFunIdx, DevsDbgObjStackFrame: () => DevsDbgObjStackFrame, DevsDbgPipe: () => DevsDbgPipe, DevsDbgPipePack: () => DevsDbgPipePack, DevsDbgProgramCounter: () => DevsDbgProgramCounter, DevsDbgReg: () => DevsDbgReg, DevsDbgRegPack: () => DevsDbgRegPack, DevsDbgStepFlags: () => DevsDbgStepFlags, DevsDbgString: () => DevsDbgString, DevsDbgSuspensionType: () => DevsDbgSuspensionType, DevsDbgValueSpecial: () => DevsDbgValueSpecial, DevsDbgValueTag: () => DevsDbgValueTag, DistanceReg: () => DistanceReg, DistanceRegPack: () => DistanceRegPack, DistanceVariant: () => DistanceVariant, DmxCmd: () => DmxCmd, DmxCmdPack: () => DmxCmdPack, DmxReg: () => DmxReg, DmxRegPack: () => DmxRegPack, DotMatrixReg: () => DotMatrixReg, DotMatrixRegPack: () => DotMatrixRegPack, DotMatrixServer: () => DotMatrixServer, DotMatrixVariant: () => DotMatrixVariant, DualMotorsReg: () => DualMotorsReg, DualMotorsRegPack: () => DualMotorsRegPack, DualMotorsServer: () => DualMotorsServer, ECO2Reg: () => ECO2Reg, ECO2RegPack: () => ECO2RegPack, ECO2Variant: () => ECO2Variant, EMBED_MIN_ASPECT_RATIO: () => EMBED_MIN_ASPECT_RATIO, ERROR: () => ERROR, ERROR_MICROBIT_INVALID_MEMORY: () => ERROR_MICROBIT_INVALID_MEMORY, ERROR_MICROBIT_JACDAC_MISSING: () => ERROR_MICROBIT_JACDAC_MISSING, ERROR_MICROBIT_UNKNOWN: () => ERROR_MICROBIT_UNKNOWN, ERROR_MICROBIT_V1: () => ERROR_MICROBIT_V1, ERROR_NO_ACK: () => ERROR_NO_ACK, ERROR_TIMEOUT: () => ERROR_TIMEOUT, ERROR_TRANSPORT_CLOSED: () => ERROR_TRANSPORT_CLOSED, ERROR_TRANSPORT_DEVICE_LOCKED: () => ERROR_TRANSPORT_DEVICE_LOCKED, ERROR_TRANSPORT_HF2_NOT_SUPPORTED: () => ERROR_TRANSPORT_HF2_NOT_SUPPORTED, ERROR_TRANSPORT_WEBSOCKET_NOT_SUPPORTED: () => ERROR_TRANSPORT_WEBSOCKET_NOT_SUPPORTED, EVENT: () => EVENT, EVENT_NODE_NAME: () => EVENT_NODE_NAME, FIELD_NODE_NAME: () => FIELD_NODE_NAME, FIRMWARE_BLOBS_CHANGE: () => FIRMWARE_BLOBS_CHANGE, FLASH_MAX_DEVICES: () => FLASH_MAX_DEVICES, FOUND: () => FOUND, FRAME_PROCESS: () => FRAME_PROCESS, FRAME_PROCESS_LARGE: () => FRAME_PROCESS_LARGE, FRAME_SEND: () => FRAME_SEND, FRAME_SEND_DISCONNECT: () => FRAME_SEND_DISCONNECT, FirmwareUpdater: () => FirmwareUpdater, Flags: () => Flags, FlexReg: () => FlexReg, FlexRegPack: () => FlexRegPack, GAMEPAD_ARCADE_BUTTONS: () => GAMEPAD_ARCADE_BUTTONS, GAMEPAD_DPAD_AB_BUTTONS: () => GAMEPAD_DPAD_AB_BUTTONS, GAMEPAD_DPAD_A_BUTTONS: () => GAMEPAD_DPAD_A_BUTTONS, GAMEPAD_DPAD_BUTTONS: () => GAMEPAD_DPAD_BUTTONS, GAMEPAD_DPAD_XY_BUTTONS: () => GAMEPAD_DPAD_XY_BUTTONS, GAMEPAD_GAMEPAD_EXTRA_BUTTONS: () => GAMEPAD_GAMEPAD_EXTRA_BUTTONS, GET_ATTEMPT: () => GET_ATTEMPT, GLOBALS_UPDATED: () => GLOBALS_UPDATED, GPIOCapabilities: () => GPIOCapabilities, GPIOCmd: () => GPIOCmd, GPIOCmdPack: () => GPIOCmdPack, GPIOMode: () => GPIOMode, GPIOReg: () => GPIOReg, GPIORegPack: () => GPIORegPack, GamepadButtons: () => GamepadButtons, GamepadEvent: () => GamepadEvent, GamepadEventPack: () => GamepadEventPack, GamepadHostManager: () => GamepadHostManager, GamepadReg: () => GamepadReg, GamepadRegPack: () => GamepadRegPack, GamepadServer: () => GamepadServer, GamepadVariant: () => GamepadVariant, GyroscopeReg: () => GyroscopeReg, GyroscopeRegPack: () => GyroscopeRegPack, HF2Proto: () => HF2Proto, HF2_CMD_BININFO: () => HF2_CMD_BININFO, HF2_CMD_CHKSUM_PAGES: () => HF2_CMD_CHKSUM_PAGES, HF2_CMD_DMESG: () => HF2_CMD_DMESG, HF2_CMD_INFO: () => HF2_CMD_INFO, HF2_CMD_JDS_CONFIG: () => HF2_CMD_JDS_CONFIG, HF2_CMD_JDS_SEND: () => HF2_CMD_JDS_SEND, HF2_CMD_READ_WORDS: () => HF2_CMD_READ_WORDS, HF2_CMD_RESET_INTO_APP: () => HF2_CMD_RESET_INTO_APP, HF2_CMD_RESET_INTO_BOOTLOADER: () => HF2_CMD_RESET_INTO_BOOTLOADER, HF2_CMD_START_FLASH: () => HF2_CMD_START_FLASH, HF2_CMD_WRITE_FLASH_PAGE: () => HF2_CMD_WRITE_FLASH_PAGE, HF2_CMD_WRITE_WORDS: () => HF2_CMD_WRITE_WORDS, HF2_DEVICE_MAJOR: () => HF2_DEVICE_MAJOR, HF2_EV_JDS_PACKET: () => HF2_EV_JDS_PACKET, HF2_EV_MASK: () => HF2_EV_MASK, HF2_FLAG_CMDPKT_BODY: () => HF2_FLAG_CMDPKT_BODY, HF2_FLAG_CMDPKT_LAST: () => HF2_FLAG_CMDPKT_LAST, HF2_FLAG_MASK: () => HF2_FLAG_MASK, HF2_FLAG_SERIAL_ERR: () => HF2_FLAG_SERIAL_ERR, HF2_FLAG_SERIAL_OUT: () => HF2_FLAG_SERIAL_OUT, HF2_MODE_BOOTLOADER: () => HF2_MODE_BOOTLOADER, HF2_MODE_USERSPACE: () => HF2_MODE_USERSPACE, HF2_SIZE_MASK: () => HF2_SIZE_MASK, HF2_STATUS_EVENT: () => HF2_STATUS_EVENT, HF2_STATUS_EXEC_ERR: () => HF2_STATUS_EXEC_ERR, HF2_STATUS_INVALID_CMD: () => HF2_STATUS_INVALID_CMD, HF2_STATUS_OK: () => HF2_STATUS_OK, HF2_TIMEOUT: () => HF2_TIMEOUT, HIDJoystickServer: () => HIDJoystickServer, HIDKeyboardServer: () => HIDKeyboardServer, HIDMouseServer: () => HIDMouseServer, HeartRateReg: () => HeartRateReg, HeartRateRegPack: () => HeartRateRegPack, HeartRateVariant: () => HeartRateVariant, HidJoystickCmd: () => HidJoystickCmd, HidJoystickCmdPack: () => HidJoystickCmdPack, HidJoystickReg: () => HidJoystickReg, HidJoystickRegPack: () => HidJoystickRegPack, HidKeyboardAction: () => HidKeyboardAction, HidKeyboardCmd: () => HidKeyboardCmd, HidKeyboardCmdPack: () => HidKeyboardCmdPack, HidKeyboardModifiers: () => HidKeyboardModifiers, HidKeyboardSelector: () => HidKeyboardSelector, HidMouseButton: () => HidMouseButton, HidMouseButtonEvent: () => HidMouseButtonEvent, HidMouseCmd: () => HidMouseCmd, HidMouseCmdPack: () => HidMouseCmdPack, HumidityReg: () => HumidityReg, HumidityRegPack: () => HumidityRegPack, I2CCmd: () => I2CCmd, I2CCmdPack: () => I2CCmdPack, I2CReg: () => I2CReg, I2CRegPack: () => I2CRegPack, I2CStatus: () => I2CStatus, IDENTIFY: () => IDENTIFY, IDENTIFY_DURATION: () => IDENTIFY_DURATION, IlluminanceReg: () => IlluminanceReg, IlluminanceRegPack: () => IlluminanceRegPack, InPipe: () => InPipe, InPipeReader: () => InPipeReader, IndexedScreenCmd: () => IndexedScreenCmd, IndexedScreenCmdPack: () => IndexedScreenCmdPack, IndexedScreenReg: () => IndexedScreenReg, IndexedScreenRegPack: () => IndexedScreenRegPack, IndexedScreenServer: () => IndexedScreenServer, JACDAC_ERROR: () => JACDAC_ERROR, JDBridge: () => JDBridge, JDBus: () => JDBus, JDCancellationToken: () => JDCancellationToken, JDClient: () => JDClient, JDDevice: () => JDDevice, JDError: () => JDError, JDEvent: () => JDEvent, JDEventSource: () => JDEventSource, JDField: () => JDField, JDNode: () => JDNode, JDRegister: () => JDRegister, JDRegisterServer: () => JDRegisterServer, JDServerServiceProvider: () => JDServerServiceProvider, JDService: () => JDService, JDServiceClient: () => JDServiceClient, JDServiceMemberNode: () => JDServiceMemberNode, JDServiceProvider: () => JDServiceProvider, JDServiceServer: () => JDServiceServer, JDSubscriptionScope: () => JDSubscriptionScope, JD_ADVERTISEMENT_0_ACK_SUPPORTED: () => JD_ADVERTISEMENT_0_ACK_SUPPORTED, JD_ADVERTISEMENT_0_COUNTER_MASK: () => JD_ADVERTISEMENT_0_COUNTER_MASK, JD_DEVICE_DISCONNECTED_DELAY: () => JD_DEVICE_DISCONNECTED_DELAY, JD_DEVICE_IDENTIFIER_BROADCAST_HIGH_MARK: () => JD_DEVICE_IDENTIFIER_BROADCAST_HIGH_MARK, JD_DEVICE_LOST_DELAY: () => JD_DEVICE_LOST_DELAY, JD_FRAME_FLAG_ACK_REQUESTED: () => JD_FRAME_FLAG_ACK_REQUESTED, JD_FRAME_FLAG_COMMAND: () => JD_FRAME_FLAG_COMMAND, JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS: () => JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS, JD_FRAME_FLAG_LOOPBACK: () => JD_FRAME_FLAG_LOOPBACK, JD_FRAME_FLAG_VNEXT: () => JD_FRAME_FLAG_VNEXT, JD_SERIAL_HEADER_SIZE: () => JD_SERIAL_HEADER_SIZE, JD_SERIAL_MAX_PAYLOAD_SIZE: () => JD_SERIAL_MAX_PAYLOAD_SIZE, JD_SERVICE_INDEX_BROADCAST: () => JD_SERVICE_INDEX_BROADCAST, JD_SERVICE_INDEX_CRC_ACK: () => JD_SERVICE_INDEX_CRC_ACK, JD_SERVICE_INDEX_CTRL: () => JD_SERVICE_INDEX_CTRL, JD_SERVICE_INDEX_INV_MASK: () => JD_SERVICE_INDEX_INV_MASK, JD_SERVICE_INDEX_MASK: () => JD_SERVICE_INDEX_MASK, JD_SERVICE_INDEX_MAX_NORMAL: () => JD_SERVICE_INDEX_MAX_NORMAL, JD_SERVICE_INDEX_PIPE: () => JD_SERVICE_INDEX_PIPE, JSONTryParse: () => JSONTryParse, KeyboardClientEvent: () => KeyboardClientEvent, KeyboardClientEventPack: () => KeyboardClientEventPack, LATE: () => LATE, LOG: () => LOG, LOST: () => LOST, LedReg: () => LedReg, LedRegPack: () => LedRegPack, LedServer: () => LedServer, LedSingleCmd: () => LedSingleCmd, LedSingleCmdPack: () => LedSingleCmdPack, LedSingleReg: () => LedSingleReg, LedSingleRegPack: () => LedSingleRegPack, LedSingleVariant: () => LedSingleVariant, LedStripCmd: () => LedStripCmd, LedStripCmdPack: () => LedStripCmdPack, LedStripLightType: () => LedStripLightType, LedStripReg: () => LedStripReg, LedStripRegPack: () => LedStripRegPack, LedStripServer: () => LedStripServer, LedStripVariant: () => LedStripVariant, LedVariant: () => LedVariant, LevelDetector: () => LevelDetector, LightBulbReg: () => LightBulbReg, LightBulbRegPack: () => LightBulbRegPack, LightLevelReg: () => LightLevelReg, LightLevelRegPack: () => LightLevelRegPack, LightLevelVariant: () => LightLevelVariant, LoggerCmd: () => LoggerCmd, LoggerCmdPack: () => LoggerCmdPack, LoggerPriority: () => LoggerPriority, LoggerReg: () => LoggerReg, LoggerRegPack: () => LoggerRegPack, LoggerServer: () => LoggerServer, MAX_SERVICES_LENGTH: () => MAX_SERVICES_LENGTH, MESSAGE: () => MESSAGE, META_ACK: () => META_ACK, META_ACK_FAILED: () => META_ACK_FAILED, META_GET: () => META_GET, META_NOT_IMPLEMENTED: () => META_NOT_IMPLEMENTED, META_PIPE: () => META_PIPE, META_TRACE: () => META_TRACE, META_TRACE_DESCRIPTION: () => META_TRACE_DESCRIPTION, MICROBIT_V2_PRODUCT_ID: () => MICROBIT_V2_PRODUCT_ID, MICROBIT_V2_VENDOR_ID: () => MICROBIT_V2_VENDOR_ID, MagneticFieldLevelEvent: () => MagneticFieldLevelEvent, MagneticFieldLevelReg: () => MagneticFieldLevelReg, MagneticFieldLevelRegPack: () => MagneticFieldLevelRegPack, MagneticFieldLevelServer: () => MagneticFieldLevelServer, MagneticFieldLevelVariant: () => MagneticFieldLevelVariant, MagnetometerCmd: () => MagnetometerCmd, MagnetometerReg: () => MagnetometerReg, MagnetometerRegPack: () => MagnetometerRegPack, MatrixKeypadEvent: () => MatrixKeypadEvent, MatrixKeypadEventPack: () => MatrixKeypadEventPack, MatrixKeypadReg: () => MatrixKeypadReg, MatrixKeypadRegPack: () => MatrixKeypadRegPack, MatrixKeypadServer: () => MatrixKeypadServer, MatrixKeypadVariant: () => MatrixKeypadVariant, MicrophoneCmd: () => MicrophoneCmd, MicrophoneCmdPack: () => MicrophoneCmdPack, MicrophoneReg: () => MicrophoneReg, MicrophoneRegPack: () => MicrophoneRegPack, MidiOutputCmd: () => MidiOutputCmd, MidiOutputCmdPack: () => MidiOutputCmdPack, MidiOutputReg: () => MidiOutputReg, MidiOutputRegPack: () => MidiOutputRegPack, ModelRunnerCmd: () => ModelRunnerCmd, ModelRunnerCmdPack: () => ModelRunnerCmdPack, ModelRunnerModelFormat: () => ModelRunnerModelFormat, ModelRunnerReg: () => ModelRunnerReg, ModelRunnerRegPack: () => ModelRunnerRegPack, MotionEvent: () => MotionEvent, MotionReg: () => MotionReg, MotionRegPack: () => MotionRegPack, MotionVariant: () => MotionVariant, MotorReg: () => MotorReg, MotorRegPack: () => MotorRegPack, MotorServer: () => MotorServer, MultitouchEvent: () => MultitouchEvent, MultitouchEventPack: () => MultitouchEventPack, MultitouchReg: () => MultitouchReg, MultitouchRegPack: () => MultitouchRegPack, NEW_LISTENER: () => NEW_LISTENER, NODESOCKET_TRANSPORT: () => NODESOCKET_TRANSPORT, NumberFormat: () => NumberFormat, OutPipe: () => OutPipe, PACKETIO_TRANSPORT: () => PACKETIO_TRANSPORT, PACKET_ANNOUNCE: () => PACKET_ANNOUNCE, PACKET_DATA_NORMALIZE: () => PACKET_DATA_NORMALIZE, PACKET_EVENT: () => PACKET_EVENT, PACKET_INVALID_CRC: () => PACKET_INVALID_CRC, PACKET_INVALID_DATA: () => PACKET_INVALID_DATA, PACKET_KIND_ANNOUNCE: () => PACKET_KIND_ANNOUNCE, PACKET_KIND_EVENT: () => PACKET_KIND_EVENT, PACKET_KIND_RO: () => PACKET_KIND_RO, PACKET_KIND_RW: () => PACKET_KIND_RW, PACKET_PRE_PROCESS: () => PACKET_PRE_PROCESS, PACKET_PROCESS: () => PACKET_PROCESS, PACKET_RECEIVE: () => PACKET_RECEIVE, PACKET_RECEIVE_ANNOUNCE: () => PACKET_RECEIVE_ANNOUNCE, PACKET_RECEIVE_NO_DEVICE: () => PACKET_RECEIVE_NO_DEVICE, PACKET_REPORT: () => PACKET_REPORT, PACKET_SEND: () => PACKET_SEND, PANIC: () => PANIC, PCControllerCmd: () => PCControllerCmd, PCControllerCmdPack: () => PCControllerCmdPack, PCControllerServer: () => PCControllerServer, PCMonitorReg: () => PCMonitorReg, PCMonitorRegPack: () => PCMonitorRegPack, PCMonitorServer: () => PCMonitorServer, PING_LOGGERS_POLL: () => PING_LOGGERS_POLL, PIPE_CLOSE_MASK: () => PIPE_CLOSE_MASK, PIPE_COUNTER_MASK: () => PIPE_COUNTER_MASK, PIPE_METADATA_MASK: () => PIPE_METADATA_MASK, PIPE_NODE_NAME: () => PIPE_NODE_NAME, PIPE_PORT_SHIFT: () => PIPE_PORT_SHIFT, PIPE_REPORT_NODE_NAME: () => PIPE_REPORT_NODE_NAME, PROGRESS: () => PROGRESS, PUBLISH: () => PUBLISH, Packet: () => Packet, PlanarPositionReg: () => PlanarPositionReg, PlanarPositionRegPack: () => PlanarPositionRegPack, PlanarPositionServer: () => PlanarPositionServer, PlanarPositionVariant: () => PlanarPositionVariant, PotentiometerReg: () => PotentiometerReg, PotentiometerRegPack: () => PotentiometerRegPack, PotentiometerVariant: () => PotentiometerVariant, PowerCmd: () => PowerCmd, PowerEvent: () => PowerEvent, PowerEventPack: () => PowerEventPack, PowerPowerStatus: () => PowerPowerStatus, PowerReg: () => PowerReg, PowerRegPack: () => PowerRegPack, PowerServer: () => PowerServer, PowerSupplyReg: () => PowerSupplyReg, PowerSupplyRegPack: () => PowerSupplyRegPack, PowerSupplyServer: () => PowerSupplyServer, PressureButtonReg: () => PressureButtonReg, PressureButtonRegPack: () => PressureButtonRegPack, PromiseBuffer: () => PromiseBuffer, PromiseQueue: () => PromiseQueue, ProtoTestCmd: () => ProtoTestCmd, ProtoTestCmdPack: () => ProtoTestCmdPack, ProtoTestEvent: () => ProtoTestEvent, ProtoTestEventPack: () => ProtoTestEventPack, ProtoTestPipe: () => ProtoTestPipe, ProtoTestPipePack: () => ProtoTestPipePack, ProtoTestReg: () => ProtoTestReg, ProtoTestRegPack: () => ProtoTestRegPack, PulseOximeterReg: () => PulseOximeterReg, PulseOximeterRegPack: () => PulseOximeterRegPack, READING_SENT: () => READING_SENT, REFRESH: () => REFRESH, REFRESH_REGISTER_POLL: () => REFRESH_REGISTER_POLL, REGISTER_NODE_NAME: () => REGISTER_NODE_NAME, REGISTER_OPTIONAL_POLL_COUNT: () => REGISTER_OPTIONAL_POLL_COUNT, REGISTER_POLL_FIRST_REPORT_INTERVAL: () => REGISTER_POLL_FIRST_REPORT_INTERVAL, REGISTER_POLL_REPORT_INTERVAL: () => REGISTER_POLL_REPORT_INTERVAL, REGISTER_POLL_REPORT_MAX_INTERVAL: () => REGISTER_POLL_REPORT_MAX_INTERVAL, REGISTER_POLL_REPORT_VOLATILE_INTERVAL: () => REGISTER_POLL_REPORT_VOLATILE_INTERVAL, REGISTER_POLL_REPORT_VOLATILE_MAX_INTERVAL: () => REGISTER_POLL_REPORT_VOLATILE_MAX_INTERVAL, REGISTER_POLL_STREAMING_INTERVAL: () => REGISTER_POLL_STREAMING_INTERVAL, REGISTER_PRE_GET: () => REGISTER_PRE_GET, REGISTER_REFRESH_RETRY_0: () => REGISTER_REFRESH_RETRY_0, REGISTER_REFRESH_RETRY_1: () => REGISTER_REFRESH_RETRY_1, REGISTER_REFRESH_TIMEOUT: () => REGISTER_REFRESH_TIMEOUT, REMOVE_LISTENER: () => REMOVE_LISTENER, RENDER: () => RENDER, REPORT_NODE_NAME: () => REPORT_NODE_NAME, REPORT_RECEIVE: () => REPORT_RECEIVE, REPORT_UPDATE: () => REPORT_UPDATE, RESET: () => RESET, RESET_IN_TIME_US: () => RESET_IN_TIME_US, RESTART: () => RESTART, ROLE_BOUND: () => ROLE_BOUND, ROLE_CHANGE: () => ROLE_CHANGE, ROLE_HAS_NO_SERVICE: () => ROLE_HAS_NO_SERVICE, ROLE_MANAGER_CHANGE: () => ROLE_MANAGER_CHANGE, ROLE_MANAGER_POLL: () => ROLE_MANAGER_POLL, ROLE_QUERY_DEVICE: () => ROLE_QUERY_DEVICE, ROLE_QUERY_SELF_DEVICE: () => ROLE_QUERY_SELF_DEVICE, ROLE_QUERY_SERVICE_INDEX: () => ROLE_QUERY_SERVICE_INDEX, ROLE_QUERY_SERVICE_OFFSET: () => ROLE_QUERY_SERVICE_OFFSET, ROLE_UNBOUND: () => ROLE_UNBOUND, RainGaugeReg: () => RainGaugeReg, RainGaugeRegPack: () => RainGaugeRegPack, RainGaugeServer: () => RainGaugeServer, RandomNumberGeneratorServer: () => RandomNumberGeneratorServer, RealTimeClockCmd: () => RealTimeClockCmd, RealTimeClockCmdPack: () => RealTimeClockCmdPack, RealTimeClockReg: () => RealTimeClockReg, RealTimeClockRegPack: () => RealTimeClockRegPack, RealTimeClockServer: () => RealTimeClockServer, RealTimeClockVariant: () => RealTimeClockVariant, ReflectedLightReg: () => ReflectedLightReg, ReflectedLightRegPack: () => ReflectedLightRegPack, ReflectedLightServer: () => ReflectedLightServer, ReflectedLightVariant: () => ReflectedLightVariant, RegisterType: () => RegisterType, RelayReg: () => RelayReg, RelayRegPack: () => RelayRegPack, RelayVariant: () => RelayVariant, RngReg: () => RngReg, RngRegPack: () => RngRegPack, RngVariant: () => RngVariant, RoleManager: () => RoleManager, RoleManagerClient: () => RoleManagerClient, RoleManagerCmd: () => RoleManagerCmd, RoleManagerCmdPack: () => RoleManagerCmdPack, RoleManagerEvent: () => RoleManagerEvent, RoleManagerPipe: () => RoleManagerPipe, RoleManagerPipePack: () => RoleManagerPipePack, RoleManagerReg: () => RoleManagerReg, RoleManagerRegPack: () => RoleManagerRegPack, RoleManagerServer: () => RoleManagerServer, RosCmd: () => RosCmd, RosCmdPack: () => RosCmdPack, RosReportMessage: () => RosReportMessage, RosServer: () => RosServer, RotaryEncoderReg: () => RotaryEncoderReg, RotaryEncoderRegPack: () => RotaryEncoderRegPack, RotaryEncoderServer: () => RotaryEncoderServer, RoverReg: () => RoverReg, RoverRegPack: () => RoverRegPack, SELF_ANNOUNCE: () => SELF_ANNOUNCE, SERIAL_TRANSPORT: () => SERIAL_TRANSPORT, SERVICE_CLIENT_ADDED: () => SERVICE_CLIENT_ADDED, SERVICE_CLIENT_REMOVED: () => SERVICE_CLIENT_REMOVED, SERVICE_MIXIN_NODE_NAME: () => SERVICE_MIXIN_NODE_NAME, SERVICE_NODE_NAME: () => SERVICE_NODE_NAME, SERVICE_PROVIDER_ADDED: () => SERVICE_PROVIDER_ADDED, SERVICE_PROVIDER_REMOVED: () => SERVICE_PROVIDER_REMOVED, SERVICE_TEST_NODE_NAME: () => SERVICE_TEST_NODE_NAME, SG90_RESPONSE_SPEED: () => SG90_RESPONSE_SPEED, SIDE_DATA: () => SIDE_DATA, SRV_ACCELEROMETER: () => SRV_ACCELEROMETER, SRV_ACIDITY: () => SRV_ACIDITY, SRV_AIR_PRESSURE: () => SRV_AIR_PRESSURE, SRV_AIR_QUALITY_INDEX: () => SRV_AIR_QUALITY_INDEX, SRV_ARCADE_GAMEPAD: () => SRV_ARCADE_GAMEPAD, SRV_ARCADE_SOUND: () => SRV_ARCADE_SOUND, SRV_BARCODE_READER: () => SRV_BARCODE_READER, SRV_BIT_RADIO: () => SRV_BIT_RADIO, SRV_BOOTLOADER: () => SRV_BOOTLOADER, SRV_BRAILLE_DISPLAY: () => SRV_BRAILLE_DISPLAY, SRV_BRIDGE: () => SRV_BRIDGE, SRV_BUTTON: () => SRV_BUTTON, SRV_BUZZER: () => SRV_BUZZER, SRV_CAPACITIVE_BUTTON: () => SRV_CAPACITIVE_BUTTON, SRV_CHARACTER_SCREEN: () => SRV_CHARACTER_SCREEN, SRV_CLOUD_ADAPTER: () => SRV_CLOUD_ADAPTER, SRV_CLOUD_CONFIGURATION: () => SRV_CLOUD_CONFIGURATION, SRV_CODAL_MESSAGE_BUS: () => SRV_CODAL_MESSAGE_BUS, SRV_COLOR: () => SRV_COLOR, SRV_COMPASS: () => SRV_COMPASS, SRV_CONTROL: () => SRV_CONTROL, SRV_DASHBOARD: () => SRV_DASHBOARD, SRV_DC_CURRENT_MEASUREMENT: () => SRV_DC_CURRENT_MEASUREMENT, SRV_DC_VOLTAGE_MEASUREMENT: () => SRV_DC_VOLTAGE_MEASUREMENT, SRV_DEVICE_SCRIPT_CONDITION: () => SRV_DEVICE_SCRIPT_CONDITION, SRV_DEVICE_SCRIPT_MANAGER: () => SRV_DEVICE_SCRIPT_MANAGER, SRV_DEVS_DBG: () => SRV_DEVS_DBG, SRV_DISTANCE: () => SRV_DISTANCE, SRV_DMX: () => SRV_DMX, SRV_DOT_MATRIX: () => SRV_DOT_MATRIX, SRV_DUAL_MOTORS: () => SRV_DUAL_MOTORS, SRV_E_CO2: () => SRV_E_CO2, SRV_FLEX: () => SRV_FLEX, SRV_GAMEPAD: () => SRV_GAMEPAD, SRV_GPIO: () => SRV_GPIO, SRV_GYROSCOPE: () => SRV_GYROSCOPE, SRV_HEART_RATE: () => SRV_HEART_RATE, SRV_HID_JOYSTICK: () => SRV_HID_JOYSTICK, SRV_HID_KEYBOARD: () => SRV_HID_KEYBOARD, SRV_HID_MOUSE: () => SRV_HID_MOUSE, SRV_HUMIDITY: () => SRV_HUMIDITY, SRV_I2C: () => SRV_I2C, SRV_ILLUMINANCE: () => SRV_ILLUMINANCE, SRV_INDEXED_SCREEN: () => SRV_INDEXED_SCREEN, SRV_INFRASTRUCTURE: () => SRV_INFRASTRUCTURE, SRV_KEYBOARD_CLIENT: () => SRV_KEYBOARD_CLIENT, SRV_LED: () => SRV_LED, SRV_LED_SINGLE: () => SRV_LED_SINGLE, SRV_LED_STRIP: () => SRV_LED_STRIP, SRV_LIGHT_BULB: () => SRV_LIGHT_BULB, SRV_LIGHT_LEVEL: () => SRV_LIGHT_LEVEL, SRV_LOGGER: () => SRV_LOGGER, SRV_MAGNETIC_FIELD_LEVEL: () => SRV_MAGNETIC_FIELD_LEVEL, SRV_MAGNETOMETER: () => SRV_MAGNETOMETER, SRV_MATRIX_KEYPAD: () => SRV_MATRIX_KEYPAD, SRV_MICROPHONE: () => SRV_MICROPHONE, SRV_MIDI_OUTPUT: () => SRV_MIDI_OUTPUT, SRV_MODEL_RUNNER: () => SRV_MODEL_RUNNER, SRV_MOTION: () => SRV_MOTION, SRV_MOTOR: () => SRV_MOTOR, SRV_MULTITOUCH: () => SRV_MULTITOUCH, SRV_PCCONTROLLER: () => SRV_PCCONTROLLER, SRV_PCMONITOR: () => SRV_PCMONITOR, SRV_PLANAR_POSITION: () => SRV_PLANAR_POSITION, SRV_POTENTIOMETER: () => SRV_POTENTIOMETER, SRV_POWER: () => SRV_POWER, SRV_POWER_SUPPLY: () => SRV_POWER_SUPPLY, SRV_PRESSURE_BUTTON: () => SRV_PRESSURE_BUTTON, SRV_PROTO_TEST: () => SRV_PROTO_TEST, SRV_PROXY: () => SRV_PROXY, SRV_PULSE_OXIMETER: () => SRV_PULSE_OXIMETER, SRV_RAIN_GAUGE: () => SRV_RAIN_GAUGE, SRV_REAL_TIME_CLOCK: () => SRV_REAL_TIME_CLOCK, SRV_REFLECTED_LIGHT: () => SRV_REFLECTED_LIGHT, SRV_RELAY: () => SRV_RELAY, SRV_RNG: () => SRV_RNG, SRV_ROLE_MANAGER: () => SRV_ROLE_MANAGER, SRV_ROS: () => SRV_ROS, SRV_ROTARY_ENCODER: () => SRV_ROTARY_ENCODER, SRV_ROVER: () => SRV_ROVER, SRV_SAT_NAV: () => SRV_SAT_NAV, SRV_SENSOR_AGGREGATOR: () => SRV_SENSOR_AGGREGATOR, SRV_SERIAL: () => SRV_SERIAL, SRV_SERVO: () => SRV_SERVO, SRV_SETTINGS: () => SRV_SETTINGS, SRV_SEVEN_SEGMENT_DISPLAY: () => SRV_SEVEN_SEGMENT_DISPLAY, SRV_SOIL_MOISTURE: () => SRV_SOIL_MOISTURE, SRV_SOLENOID: () => SRV_SOLENOID, SRV_SOUND_LEVEL: () => SRV_SOUND_LEVEL, SRV_SOUND_PLAYER: () => SRV_SOUND_PLAYER, SRV_SOUND_RECORDER_WITH_PLAYBACK: () => SRV_SOUND_RECORDER_WITH_PLAYBACK, SRV_SOUND_SPECTRUM: () => SRV_SOUND_SPECTRUM, SRV_SPEECH_SYNTHESIS: () => SRV_SPEECH_SYNTHESIS, SRV_SWITCH: () => SRV_SWITCH, SRV_TCP: () => SRV_TCP, SRV_TEMPERATURE: () => SRV_TEMPERATURE, SRV_TIMESERIES_AGGREGATOR: () => SRV_TIMESERIES_AGGREGATOR, SRV_TRAFFIC_LIGHT: () => SRV_TRAFFIC_LIGHT, SRV_TVOC: () => SRV_TVOC, SRV_UNIQUE_BRAIN: () => SRV_UNIQUE_BRAIN, SRV_USB_BRIDGE: () => SRV_USB_BRIDGE, SRV_UV_INDEX: () => SRV_UV_INDEX, SRV_VERIFIED_TELEMETRY: () => SRV_VERIFIED_TELEMETRY, SRV_VIBRATION_MOTOR: () => SRV_VIBRATION_MOTOR, SRV_WATER_LEVEL: () => SRV_WATER_LEVEL, SRV_WEIGHT_SCALE: () => SRV_WEIGHT_SCALE, SRV_WIFI: () => SRV_WIFI, SRV_WIND_DIRECTION: () => SRV_WIND_DIRECTION, SRV_WIND_SPEED: () => SRV_WIND_SPEED, SRV_WSSK: () => SRV_WSSK, START: () => START, STATE_CHANGE: () => STATE_CHANGE, STOP: () => STOP, STREAMING_DEFAULT_INTERVAL: () => STREAMING_DEFAULT_INTERVAL, SUBSCRIBE: () => SUBSCRIBE, SatNavEvent: () => SatNavEvent, SatNavReg: () => SatNavReg, SatNavRegPack: () => SatNavRegPack, SatNavServer: () => SatNavServer, SensorAggregatorReg: () => SensorAggregatorReg, SensorAggregatorRegPack: () => SensorAggregatorRegPack, SensorAggregatorSampleType: () => SensorAggregatorSampleType, SensorReg: () => SensorReg, SensorRegPack: () => SensorRegPack, SensorServer: () => SensorServer, SerialCmd: () => SerialCmd, SerialCmdPack: () => SerialCmdPack, SerialParityType: () => SerialParityType, SerialReg: () => SerialReg, SerialRegPack: () => SerialRegPack, SerialServer: () => SerialServer, ServoReg: () => ServoReg, ServoRegPack: () => ServoRegPack, ServoServer: () => ServoServer, SettingsClient: () => SettingsClient, SettingsCmd: () => SettingsCmd, SettingsCmdPack: () => SettingsCmdPack, SettingsEvent: () => SettingsEvent, SettingsPipe: () => SettingsPipe, SettingsPipePack: () => SettingsPipePack, SettingsServer: () => SettingsServer, SevenSegmentDisplayCmd: () => SevenSegmentDisplayCmd, SevenSegmentDisplayCmdPack: () => SevenSegmentDisplayCmdPack, SevenSegmentDisplayReg: () => SevenSegmentDisplayReg, SevenSegmentDisplayRegPack: () => SevenSegmentDisplayRegPack, SevenSegmentDisplayServer: () => SevenSegmentDisplayServer, SoilMoistureReg: () => SoilMoistureReg, SoilMoistureRegPack: () => SoilMoistureRegPack, SoilMoistureVariant: () => SoilMoistureVariant, SolenoidReg: () => SolenoidReg, SolenoidRegPack: () => SolenoidRegPack, SolenoidVariant: () => SolenoidVariant, SoundLevelEvent: () => SoundLevelEvent, SoundLevelReg: () => SoundLevelReg, SoundLevelRegPack: () => SoundLevelRegPack, SoundPlayerCmd: () => SoundPlayerCmd, SoundPlayerCmdPack: () => SoundPlayerCmdPack, SoundPlayerPipe: () => SoundPlayerPipe, SoundPlayerPipePack: () => SoundPlayerPipePack, SoundPlayerReg: () => SoundPlayerReg, SoundPlayerRegPack: () => SoundPlayerRegPack, SoundPlayerServer: () => SoundPlayerServer, SoundRecorderWithPlaybackCmd: () => SoundRecorderWithPlaybackCmd, SoundRecorderWithPlaybackCmdPack: () => SoundRecorderWithPlaybackCmdPack, SoundRecorderWithPlaybackReg: () => SoundRecorderWithPlaybackReg, SoundRecorderWithPlaybackRegPack: () => SoundRecorderWithPlaybackRegPack, SoundRecorderWithPlaybackStatus: () => SoundRecorderWithPlaybackStatus, SoundSpectrumReg: () => SoundSpectrumReg, SoundSpectrumRegPack: () => SoundSpectrumRegPack, SpeechSynthesisCmd: () => SpeechSynthesisCmd, SpeechSynthesisCmdPack: () => SpeechSynthesisCmdPack, SpeechSynthesisReg: () => SpeechSynthesisReg, SpeechSynthesisRegPack: () => SpeechSynthesisRegPack, SpeechSynthesisServer: () => SpeechSynthesisServer, SwitchEvent: () => SwitchEvent, SwitchReg: () => SwitchReg, SwitchRegPack: () => SwitchRegPack, SwitchServer: () => SwitchServer, SwitchVariant: () => SwitchVariant, SystemCmd: () => SystemCmd, SystemCmdPack: () => SystemCmdPack, SystemEvent: () => SystemEvent, SystemEventPack: () => SystemEventPack, SystemReadingThreshold: () => SystemReadingThreshold, SystemReg: () => SystemReg, SystemRegPack: () => SystemRegPack, SystemStatusCodes: () => SystemStatusCodes, TIMEOUT: () => TIMEOUT, TIMEOUT_DISCONNECT: () => TIMEOUT_DISCONNECT, TRACE: () => TRACE, TRACE_FILTER_HORIZON: () => TRACE_FILTER_HORIZON, TRANSPORT_CONNECT_RETRY_DELAY: () => TRANSPORT_CONNECT_RETRY_DELAY, TRANSPORT_ERROR: () => TRANSPORT_ERROR, TYPESCRIPT_STATIC_NAMESPACE: () => TYPESCRIPT_STATIC_NAMESPACE, TcpCmd: () => TcpCmd, TcpCmdPack: () => TcpCmdPack, TcpPipe: () => TcpPipe, TcpPipeCmd: () => TcpPipeCmd, TcpPipeCmdPack: () => TcpPipeCmdPack, TcpPipePack: () => TcpPipePack, TcpTcpError: () => TcpTcpError, TemperatureReg: () => TemperatureReg, TemperatureRegPack: () => TemperatureRegPack, TemperatureVariant: () => TemperatureVariant, TimeseriesAggregatorCmd: () => TimeseriesAggregatorCmd, TimeseriesAggregatorCmdPack: () => TimeseriesAggregatorCmdPack, TimeseriesAggregatorReg: () => TimeseriesAggregatorReg, TimeseriesAggregatorRegPack: () => TimeseriesAggregatorRegPack, Trace: () => Trace, TracePlayer: () => TracePlayer, TraceRecorder: () => TraceRecorder, TraceView: () => TraceView, TrafficLightReg: () => TrafficLightReg, TrafficLightRegPack: () => TrafficLightRegPack, TrafficLightServer: () => TrafficLightServer, Transport: () => Transport, TvocReg: () => TvocReg, TvocRegPack: () => TvocRegPack, UNBOUND: () => UNBOUND, UPLOAD_BIN: () => UPLOAD_BIN, UPLOAD_JSON: () => UPLOAD_JSON, USBIO: () => USBIO, USB_FILTERS: () => USB_FILTERS, USB_TRANSPORT: () => USB_TRANSPORT, UsbBridgeCmd: () => UsbBridgeCmd, UsbBridgeQByte: () => UsbBridgeQByte, UvIndexReg: () => UvIndexReg, UvIndexRegPack: () => UvIndexRegPack, UvIndexVariant: () => UvIndexVariant, VIRTUAL_DEVICE_NODE_NAME: () => VIRTUAL_DEVICE_NODE_NAME, VerifiedTelemetryCmd: () => VerifiedTelemetryCmd, VerifiedTelemetryEvent: () => VerifiedTelemetryEvent, VerifiedTelemetryEventPack: () => VerifiedTelemetryEventPack, VerifiedTelemetryFingerprintType: () => VerifiedTelemetryFingerprintType, VerifiedTelemetryReg: () => VerifiedTelemetryReg, VerifiedTelemetryRegPack: () => VerifiedTelemetryRegPack, VerifiedTelemetryServer: () => VerifiedTelemetryServer, VerifiedTelemetryStatus: () => VerifiedTelemetryStatus, VibrationMotorCmd: () => VibrationMotorCmd, VibrationMotorCmdPack: () => VibrationMotorCmdPack, VibrationMotorReg: () => VibrationMotorReg, VibrationMotorRegPack: () => VibrationMotorRegPack, VibrationMotorServer: () => VibrationMotorServer, WEBSOCKET_TRANSPORT: () => WEBSOCKET_TRANSPORT, WallClockScheduler: () => WallClockScheduler, WaterLevelReg: () => WaterLevelReg, WaterLevelRegPack: () => WaterLevelRegPack, WaterLevelVariant: () => WaterLevelVariant, WebSerialIO: () => WebSerialIO, WebSerialTransport: () => WebSerialTransport, WebSocketBridge: () => WebSocketBridge, WebSocketTransport: () => WebSocketTransport, WeightScaleCmd: () => WeightScaleCmd, WeightScaleCmdPack: () => WeightScaleCmdPack, WeightScaleReg: () => WeightScaleReg, WeightScaleRegPack: () => WeightScaleRegPack, WeightScaleVariant: () => WeightScaleVariant, WifiAPFlags: () => WifiAPFlags, WifiCmd: () => WifiCmd, WifiCmdPack: () => WifiCmdPack, WifiEvent: () => WifiEvent, WifiEventPack: () => WifiEventPack, WifiPipe: () => WifiPipe, WifiPipePack: () => WifiPipePack, WifiReg: () => WifiReg, WifiRegPack: () => WifiRegPack, WifiServer: () => WifiServer, WindDirectionReg: () => WindDirectionReg, WindDirectionRegPack: () => WindDirectionRegPack, WindSpeedReg: () => WindSpeedReg, WindSpeedRegPack: () => WindSpeedRegPack, WsskCmd: () => WsskCmd, WsskCmdPack: () => WsskCmdPack, WsskDataType: () => WsskDataType, WsskStreamingType: () => WsskStreamingType, addComment: () => addComment, addServer: () => addServer, addServiceProvider: () => addServiceProvider, addServiceProviderDefinition: () => addServiceProviderDefinition, anyRandomUint32: () => anyRandomUint32, arrayConcatMany: () => arrayConcatMany, arrayEq: () => arrayEq, arrayShuffle: () => arrayShuffle, arrayify: () => arrayify, assert: () => assert, bufferConcat: () => bufferConcat, bufferConcatMany: () => bufferConcatMany, bufferEq: () => bufferEq, bufferToArray: () => bufferToArray, bufferToString: () => bufferToString, cStorage: () => cStorage, camelize: () => camelize, capitalize: () => capitalize, clampToStorage: () => clampToStorage, cleanStack: () => cleanStack, clone: () => clone, commandName: () => commandName, concatBufferArray: () => concatBufferArray, converters: () => converters, crc: () => crc, createBluetoothTransport: () => createBluetoothTransport, createIFrameBridge: () => createIFrameBridge, createNodeSPITransport: () => createNodeSPITransport, createNodeSocketTransport: () => createNodeSocketTransport, createNodeUSBOptions: () => createNodeUSBOptions, createNodeWebSerialTransport: () => createNodeWebSerialTransport, createProxyBridge: () => createProxyBridge, createUSBBus: () => createUSBBus, createUSBTransport: () => createUSBTransport, createUSBWorkerTransport: () => createUSBWorkerTransport, createWebBus: () => createWebBus, createWebSerialTransport: () => createWebSerialTransport, createWebSocketBus: () => createWebSocketBus, createWebSocketTransport: () => createWebSocketTransport, dashify: () => dashify, dateToClock: () => dateToClock, debounce: () => debounce, debounceAsync: () => debounceAsync, decodeMember: () => decodeMember, decodeMembers: () => decodeMembers, decodePacketData: () => decodePacketData, decodeU32LE: () => decodeU32LE, delay: () => delay, dependencyId: () => dependencyId, deviceCatalog: () => deviceCatalog, deviceCatalogImage: () => deviceCatalogImage, deviceServiceName: () => deviceServiceName, dualDeviceId: () => dualDeviceId, ellipse: () => ellipse, ellipseFirstSentence: () => ellipseFirstSentence, ellipseJoin: () => ellipseJoin, encodeU32LE: () => encodeU32LE, encodings: () => encodings, errorCode: () => errorCode, escapeDeviceIdentifier: () => escapeDeviceIdentifier, escapeDeviceNameIdentifier: () => escapeDeviceNameIdentifier, escapeRoleName: () => escapeRoleName, flagsToValue: () => flagsToValue, flatClone: () => flatClone, fnv1: () => fnv1, fromBase64: () => fromBase64, fromHex: () => fromHex, fromUTF8: () => fromUTF8, genFieldInfo: () => genFieldInfo, generateDeviceSpecificationId: () => generateDeviceSpecificationId, getBit: () => getBit, getNumber: () => getNumber, groupBy: () => groupBy, hash: () => hash, hexDump: () => hexDump, hexNum: () => hexNum, hsvToCss: () => hsvToCss, humanify: () => humanify, identifierToUrlPath: () => identifierToUrlPath, idiv: () => idiv, inIFrame: () => inIFrame, injectDevTools: () => injectDevTools, intOfBuffer: () => intOfBuffer, isAckError: () => isAckError, isActuator: () => isActuator, isBufferEmpty: () => isBufferEmpty, isCancelError: () => isCancelError, isCodeError: () => isCodeError, isCommand: () => isCommand, isConstRegister: () => isConstRegister, isDeviceId: () => isDeviceId, isDualDeviceId: () => isDualDeviceId, isEvent: () => isEvent, isHighLevelEvent: () => isHighLevelEvent, isHighLevelRegister: () => isHighLevelRegister, isInfrastructure: () => isInfrastructure, isInstanceOf: () => isInstanceOf, isIntegerType: () => isIntegerType, isIntensity: () => isIntensity, isLargeFrame: () => isLargeFrame, isNumericType: () => isNumericType, isOptionalReadingRegisterCode: () => isOptionalReadingRegisterCode, isPipeReport: () => isPipeReport, isPipeReportOf: () => isPipeReportOf, isReadOnlyRegister: () => isReadOnlyRegister, isReading: () => isReading, isRegister: () => isRegister2, isReportOf: () => isReportOf, isSensor: () => isSensor, isSet: () => isSet, isTimeoutError: () => isTimeoutError, isValue: () => isValue, isValueOrIntensity: () => isValueOrIntensity, isWebBluetoothEnabled: () => isWebBluetoothEnabled, isWebBluetoothSupported: () => isWebBluetoothSupported, isWebSerialEnabled: () => isWebSerialEnabled, isWebSerialSupported: () => isWebSerialSupported, isWebSocketTransportSupported: () => isWebSocketTransportSupported, isWebTransportSupported: () => isWebTransportSupported, isWebUSBEnabled: () => isWebUSBEnabled, isWebUSBSupported: () => isWebUSBSupported, jdpack: () => jdpack, jdpackEqual: () => jdpackEqual, jdunpack: () => jdunpack, jsQuote: () => jsQuote, jsonCopyFrom: () => jsonCopyFrom, lightEncode: () => lightEncode, loadServiceSpecifications: () => loadServiceSpecifications, localStorageSetting: () => localStorageSetting, memberValueToString: () => memberValueToString, memcpy: () => memcpy, modifierCodes: () => modifierCodes, nodeSetting: () => nodeSetting, normalizeDeviceSpecification: () => normalizeDeviceSpecification, numberFormatFromStorageType: () => numberFormatFromStorageType, numberFormatToStorageType: () => numberFormatToStorageType, objectToUnpacked: () => objectToUnpacked, packArguments: () => packArguments, packFormat: () => packFormat, packInfo: () => packInfo, packedValuesIsEqual: () => packedValuesIsEqual, packetSpeedTest: () => packetSpeedTest, parseDeviceId: () => parseDeviceId, parseDualDeviceId: () => parseDualDeviceId, parseFirmwareFile: () => parseFirmwareFile, parseIdentifier: () => parseIdentifier, parseLogicLog: () => parseLogicLog, parsePacketFilter: () => parsePacketFilter, parseServiceSpecificationMarkdownToJSON: () => parseServiceSpecificationMarkdownToJSON, parseTrace: () => parseTrace, parseUF2Firmware: () => parseUF2Firmware, pick: () => pick, prettyDuration: () => prettyDuration, prettyEnum: () => prettyEnum, prettyMemberUnit: () => prettyMemberUnit, prettyMicroDuration: () => prettyMicroDuration, prettySize: () => prettySize, prettyUnit: () => prettyUnit2, printPacket: () => printPacket, randomBytes: () => randomBytes, randomDeviceId: () => randomDeviceId, randomRange: () => randomRange, randomUInt: () => randomUInt, range: () => range, read16: () => read16, read32: () => read32, readBlobToText: () => readBlobToText, readBlobToUint8Array: () => readBlobToUint8Array, renderHidMouseButtons: () => renderHidMouseButtons, renderKeyboardKey: () => renderKeyboardKey, renderWithPrecision: () => renderWithPrecision, replayLogicLog: () => replayLogicLog, resolveRoleService: () => resolveRoleService, resolveUnit: () => resolveUnit, reverseSelectors: () => reverseSelectors, rgbToHtmlColor: () => rgbToHtmlColor, rgbaToHtmlColor: () => rgbaToHtmlColor, roundWithPrecision: () => roundWithPrecision, scaleFloatToInt: () => scaleFloatToInt, scaleIntToFloat: () => scaleIntToFloat, secondaryUnitConverters: () => secondaryUnitConverters, selectors: () => selectors, semverCmp: () => semverCmp, sendStayInBootloaderCommand: () => sendStayInBootloaderCommand, sensorSpecifications: () => sensorSpecifications, serializeToTrace: () => serializeToTrace, serviceClass: () => serviceClass, serviceMap: () => serviceMap, serviceName: () => serviceName, serviceProviderDefinitionFromServiceClass: () => serviceProviderDefinitionFromServiceClass, serviceProviderDefinitions: () => serviceProviderDefinitions, serviceShortIdOrClass: () => serviceShortIdOrClass, serviceSpecificationFromClassIdentifier: () => serviceSpecificationFromClassIdentifier, serviceSpecificationFromName: () => serviceSpecificationFromName, serviceSpecifications: () => serviceSpecifications, setBit: () => setBit, setNumber: () => setNumber, sevenSegmentDigitEncode: () => sevenSegmentDigitEncode, sha256: () => sha256, sha256Hmac: () => sha256Hmac, shortDeviceId: () => shortDeviceId, signal: () => signal, sizeOfNumberFormat: () => sizeOfNumberFormat, snakify: () => snakify, snapshotSensors: () => snapshotSensors, splitFilter: () => splitFilter, stack: () => stack, startDevTools: () => startDevTools, startRoles: () => startRoles, startServiceProviderFromServiceClass: () => startServiceProviderFromServiceClass, storageTypeRange: () => storageTypeRange, strcmp: () => strcmp, stringToBuffer: () => stringToBuffer, stringToUint8Array: () => stringToUint8Array, throttle: () => throttle, throwError: () => throwError, toArray: () => toArray, toAscii: () => toAscii, toBase64: () => toBase64, toFullHex: () => toFullHex, toHex: () => toHex2, toMap: () => toMap, toUTF8: () => toUTF8, toggleBit: () => toggleBit, tonePayload: () => tonePayload, tryParseMemberValue: () => tryParseMemberValue, uint8ArrayToString: () => uint8ArrayToString, uintOfBuffer: () => uintOfBuffer, unique: () => unique, uniqueMap: () => uniqueMap, uniqueName: () => uniqueName, unitDescription: () => unitDescription, units: () => units, unpackedToObject: () => unpackedToObject, updateApplicable: () => updateApplicable, usbRequestDevice: () => usbRequestDevice, valueToFlags: () => valueToFlags, versionTryParse: () => versionTryParse, watchLocation: () => watchLocation, wrapComment: () => wrapComment, wrapDecodedMembers: () => wrapDecodedMembers, wrapSnippet: () => wrapSnippet, write16: () => write16, write24: () => write24, write32: () => write32 }); module.exports = __toCommonJS(jacdac_exports); // jacdac-spec/dist/specconstants.ts var CONST_SYSTEM_ANNOUNCE_INTERVAL = 500; var SystemReadingThreshold = /* @__PURE__ */ ((SystemReadingThreshold2) => { SystemReadingThreshold2[SystemReadingThreshold2["Neutral"] = 1] = "Neutral"; SystemReadingThreshold2[SystemReadingThreshold2["Inactive"] = 2] = "Inactive"; SystemReadingThreshold2[SystemReadingThreshold2["Active"] = 3] = "Active"; return SystemReadingThreshold2; })(SystemReadingThreshold || {}); var SystemStatusCodes = /* @__PURE__ */ ((SystemStatusCodes3) => { SystemStatusCodes3[SystemStatusCodes3["Ready"] = 0] = "Ready"; SystemStatusCodes3[SystemStatusCodes3["Initializing"] = 1] = "Initializing"; SystemStatusCodes3[SystemStatusCodes3["Calibrating"] = 2] = "Calibrating"; SystemStatusCodes3[SystemStatusCodes3["Sleeping"] = 3] = "Sleeping"; SystemStatusCodes3[SystemStatusCodes3["WaitingForInput"] = 4] = "WaitingForInput"; SystemStatusCodes3[SystemStatusCodes3["CalibrationNeeded"] = 100] = "CalibrationNeeded"; return SystemStatusCodes3; })(SystemStatusCodes || {}); var SystemCmd = /* @__PURE__ */ ((SystemCmd2) => { SystemCmd2[SystemCmd2["Announce"] = 0] = "Announce"; SystemCmd2[SystemCmd2["GetRegister"] = 4096] = "GetRegister"; SystemCmd2[SystemCmd2["SetRegister"] = 8192] = "SetRegister"; SystemCmd2[SystemCmd2["Calibrate"] = 2] = "Calibrate"; SystemCmd2[SystemCmd2["CommandNotImplemented"] = 3] = "CommandNotImplemented"; return SystemCmd2; })(SystemCmd || {}); var SystemCmdPack; ((SystemCmdPack2) => { SystemCmdPack2.CommandNotImplemented = "u16 u16"; })(SystemCmdPack || (SystemCmdPack = {})); var SystemReg = /* @__PURE__ */ ((SystemReg2) => { SystemReg2[SystemReg2["Intensity"] = 1] = "Intensity"; SystemReg2[SystemReg2["Value"] = 2] = "Value"; SystemReg2[SystemReg2["MinValue"] = 272] = "MinValue"; SystemReg2[SystemReg2["MaxValue"] = 273] = "MaxValue"; SystemReg2[SystemReg2["MaxPower"] = 7] = "MaxPower"; SystemReg2[SystemReg2["StreamingSamples"] = 3] = "StreamingSamples"; SystemReg2[SystemReg2["StreamingInterval"] = 4] = "StreamingInterval"; SystemReg2[SystemReg2["Reading"] = 257] = "Reading"; SystemReg2[SystemReg2["ReadingRange"] = 8] = "ReadingRange"; SystemReg2[SystemReg2["SupportedRanges"] = 266] = "SupportedRanges"; SystemReg2[SystemReg2["MinReading"] = 260] = "MinReading"; SystemReg2[SystemReg2["MaxReading"] = 261] = "MaxReading"; SystemReg2[SystemReg2["ReadingError"] = 262] = "ReadingError"; SystemReg2[SystemReg2["ReadingResolution"] = 264] = "ReadingResolution"; SystemReg2[SystemReg2["InactiveThreshold"] = 5] = "InactiveThreshold"; SystemReg2[SystemReg2["ActiveThreshold"] = 6] = "ActiveThreshold"; SystemReg2[SystemReg2["StreamingPreferredInterval"] = 258] = "StreamingPreferredInterval"; SystemReg2[SystemReg2["Variant"] = 263] = "Variant"; SystemReg2[SystemReg2["ClientVariant"] = 9] = "ClientVariant"; SystemReg2[SystemReg2["StatusCode"] = 259] = "StatusCode"; SystemReg2[SystemReg2["InstanceName"] = 265] = "InstanceName"; return SystemReg2; })(SystemReg || {}); var SystemRegPack; ((SystemRegPack2) => { SystemRegPack2.Intensity = "u32"; SystemRegPack2.Value = "i32"; SystemRegPack2.MinValue = "i32"; SystemRegPack2.MaxValue = "i32"; SystemRegPack2.MaxPower = "u16"; SystemRegPack2.StreamingSamples = "u8"; SystemRegPack2.StreamingInterval = "u32"; SystemRegPack2.Reading = "i32"; SystemRegPack2.ReadingRange = "u32"; SystemRegPack2.SupportedRanges = "r: u32"; SystemRegPack2.MinReading = "i32"; SystemRegPack2.MaxReading = "i32"; SystemRegPack2.ReadingError = "u32"; SystemRegPack2.ReadingResolution = "u32"; SystemRegPack2.InactiveThreshold = "i32"; SystemRegPack2.ActiveThreshold = "i32"; SystemRegPack2.StreamingPreferredInterval = "u32"; SystemRegPack2.Variant = "u32"; SystemRegPack2.ClientVariant = "s"; SystemRegPack2.StatusCode = "u16 u16"; SystemRegPack2.InstanceName = "s"; })(SystemRegPack || (SystemRegPack = {})); var SystemEvent = /* @__PURE__ */ ((SystemEvent2) => { SystemEvent2[SystemEvent2["Active"] = 1] = "Active"; SystemEvent2[SystemEvent2["Inactive"] = 2] = "Inactive"; SystemEvent2[SystemEvent2["Change"] = 3] = "Change"; SystemEvent2[SystemEvent2["StatusCodeChanged"] = 4] = "StatusCodeChanged"; SystemEvent2[SystemEvent2["Neutral"] = 7] = "Neutral"; return SystemEvent2; })(SystemEvent || {}); var SystemEventPack; ((SystemEventPack2) => { SystemEventPack2.StatusCodeChanged = "u16 u16"; })(SystemEventPack || (SystemEventPack = {})); var BaseCmd = /* @__PURE__ */ ((BaseCmd2) => { BaseCmd2[BaseCmd2["CommandNotImplemented"] = 3] = "CommandNotImplemented"; return BaseCmd2; })(BaseCmd || {}); var BaseCmdPack; ((BaseCmdPack2) => { BaseCmdPack2.CommandNotImplemented = "u16 u16"; })(BaseCmdPack || (BaseCmdPack = {})); var BaseReg = /* @__PURE__ */ ((BaseReg2) => { BaseReg2[BaseReg2["InstanceName"] = 265] = "InstanceName"; BaseReg2[BaseReg2["StatusCode"] = 259] = "StatusCode"; BaseReg2[BaseReg2["ClientVariant"] = 9] = "ClientVariant"; return BaseReg2; })(BaseReg || {}); var BaseRegPack; ((BaseRegPack2) => { BaseRegPack2.InstanceName = "s"; BaseRegPack2.StatusCode = "u16 u16"; BaseRegPack2.ClientVariant = "s"; })(BaseRegPack || (BaseRegPack = {})); var BaseEvent = /* @__PURE__ */ ((BaseEvent2) => { BaseEvent2[BaseEvent2["StatusCodeChanged"] = 4] = "StatusCodeChanged"; return BaseEvent2; })(BaseEvent || {}); var BaseEventPack; ((BaseEventPack2) => { BaseEventPack2.StatusCodeChanged = "u16 u16"; })(BaseEventPack || (BaseEventPack = {})); var SensorReg = /* @__PURE__ */ ((SensorReg2) => { SensorReg2[SensorReg2["StreamingSamples"] = 3] = "StreamingSamples"; SensorReg2[SensorReg2["StreamingInterval"] = 4] = "StreamingInterval"; SensorReg2[SensorReg2["StreamingPreferredInterval"] = 258] = "StreamingPreferredInterval"; return SensorReg2; })(SensorReg || {}); var SensorRegPack; ((SensorRegPack2) => { SensorRegPack2.StreamingSamples = "u8"; SensorRegPack2.StreamingInterval = "u32"; SensorRegPack2.StreamingPreferredInterval = "u32"; })(SensorRegPack || (SensorRegPack = {})); var SRV_ACCELEROMETER = 521405449; var AccelerometerReg = /* @__PURE__ */ ((AccelerometerReg2) => { AccelerometerReg2[AccelerometerReg2["Forces"] = 257] = "Forces"; AccelerometerReg2[AccelerometerReg2["ForcesError"] = 262] = "ForcesError"; AccelerometerReg2[AccelerometerReg2["MaxForce"] = 8] = "MaxForce"; AccelerometerReg2[AccelerometerReg2["MaxForcesSupported"] = 266] = "MaxForcesSupported"; return AccelerometerReg2; })(AccelerometerReg || {}); var AccelerometerRegPack; ((AccelerometerRegPack2) => { AccelerometerRegPack2.Forces = "i12.20 i12.20 i12.20"; AccelerometerRegPack2.ForcesError = "u12.20"; AccelerometerRegPack2.MaxForce = "u12.20"; AccelerometerRegPack2.MaxForcesSupported = "r: u12.20"; })(AccelerometerRegPack || (AccelerometerRegPack = {})); var AccelerometerEvent = /* @__PURE__ */ ((AccelerometerEvent2) => { AccelerometerEvent2[AccelerometerEvent2["TiltUp"] = 129] = "TiltUp"; AccelerometerEvent2[AccelerometerEvent2["TiltDown"] = 130] = "TiltDown"; AccelerometerEvent2[AccelerometerEvent2["TiltLeft"] = 131] = "TiltLeft"; AccelerometerEvent2[AccelerometerEvent2["TiltRight"] = 132] = "TiltRight"; AccelerometerEvent2[AccelerometerEvent2["FaceUp"] = 133] = "FaceUp"; AccelerometerEvent2[AccelerometerEvent2["FaceDown"] = 134] = "FaceDown"; AccelerometerEvent2[AccelerometerEvent2["Freefall"] = 135] = "Freefall"; AccelerometerEvent2[AccelerometerEvent2["Shake"] = 139] = "Shake"; AccelerometerEvent2[AccelerometerEvent2["Force2g"] = 140] = "Force2g"; AccelerometerEvent2[AccelerometerEvent2["Force3g"] = 136] = "Force3g"; AccelerometerEvent2[AccelerometerEvent2["Force6g"] = 137] = "Force6g"; AccelerometerEvent2[AccelerometerEvent2["Force8g"] = 138] = "Force8g"; return AccelerometerEvent2; })(AccelerometerEvent || {}); var SRV_ACIDITY = 513243333; var AcidityReg = /* @__PURE__ */ ((AcidityReg2) => { AcidityReg2[AcidityReg2["Acidity"] = 257] = "Acidity"; AcidityReg2[AcidityReg2["AcidityError"] = 262] = "AcidityError"; AcidityReg2[AcidityReg2["MinAcidity"] = 260] = "MinAcidity"; AcidityReg2[AcidityReg2["MaxHumidity"] = 261] = "MaxHumidity"; return AcidityReg2; })(AcidityReg || {}); var AcidityRegPack; ((AcidityRegPack2) => { AcidityRegPack2.Acidity = "u4.12"; AcidityRegPack2.AcidityError = "u4.12"; AcidityRegPack2.MinAcidity = "u4.12"; AcidityRegPack2.MaxHumidity = "u4.12"; })(AcidityRegPack || (AcidityRegPack = {})); var SRV_AIR_PRESSURE = 504462570; var AirPressureReg = /* @__PURE__ */ ((AirPressureReg2) => { AirPressureReg2[AirPressureReg2["Pressure"] = 257] = "Pressure"; AirPressureReg2[AirPressureReg2["PressureError"] = 262] = "PressureError"; AirPressureReg2[AirPressureReg2["MinPressure"] = 260] = "MinPressure"; AirPressureReg2[AirPressureReg2["MaxPressure"] = 261] = "MaxPressure"; return AirPressureReg2; })(AirPressureReg || {}); var AirPressureRegPack; ((AirPressureRegPack2) => { AirPressureRegPack2.Pressure = "u22.10"; AirPressureRegPack2.PressureError = "u22.10"; AirPressureRegPack2.MinPressure = "u22.10"; AirPressureRegPack2.MaxPressure = "u22.10"; })(AirPressureRegPack || (AirPressureRegPack = {})); var SRV_AIR_QUALITY_INDEX = 346844886; var AirQualityIndexReg = /* @__PURE__ */ ((AirQualityIndexReg2) => { AirQualityIndexReg2[AirQualityIndexReg2["AqiIndex"] = 257] = "AqiIndex"; AirQualityIndexReg2[AirQualityIndexReg2["AqiIndexError"] = 262] = "AqiIndexError"; AirQualityIndexReg2[AirQualityIndexReg2["MinAqiIndex"] = 260] = "MinAqiIndex"; AirQualityIndexReg2[AirQualityIndexReg2["MaxAqiIndex"] = 261] = "MaxAqiIndex"; return AirQualityIndexReg2; })(AirQualityIndexReg || {}); var AirQualityIndexRegPack; ((AirQualityIndexRegPack2) => { AirQualityIndexRegPack2.AqiIndex = "u16.16"; AirQualityIndexRegPack2.AqiIndexError = "u16.16"; AirQualityIndexRegPack2.MinAqiIndex = "u16.16"; AirQualityIndexRegPack2.MaxAqiIndex = "u16.16"; })(AirQualityIndexRegPack || (AirQualityIndexRegPack = {})); var SRV_ARCADE_GAMEPAD = 501915758; var ArcadeGamepadButton = /* @__PURE__ */ ((ArcadeGamepadButton2) => { ArcadeGamepadButton2[ArcadeGamepadButton2["Left"] = 1] = "Left"; ArcadeGamepadButton2[ArcadeGamepadButton2["Up"] = 2] = "Up"; ArcadeGamepadButton2[ArcadeGamepadButton2["Right"] = 3] = "Right"; ArcadeGamepadButton2[ArcadeGamepadButton2["Down"] = 4] = "Down"; ArcadeGamepadButton2[ArcadeGamepadButton2["A"] = 5] = "A"; ArcadeGamepadButton2[ArcadeGamepadButton2["B"] = 6] = "B"; ArcadeGamepadButton2[ArcadeGamepadButton2["Menu"] = 7] = "Menu"; ArcadeGamepadButton2[ArcadeGamepadButton2["Select"] = 8] = "Select"; ArcadeGamepadButton2[ArcadeGamepadButton2["Reset"] = 9] = "Reset"; ArcadeGamepadButton2[ArcadeGamepadButton2["Exit"] = 10] = "Exit"; return ArcadeGamepadButton2; })(ArcadeGamepadButton || {}); var ArcadeGamepadReg = /* @__PURE__ */ ((ArcadeGamepadReg2) => { ArcadeGamepadReg2[ArcadeGamepadReg2["Buttons"] = 257] = "Buttons"; ArcadeGamepadReg2[ArcadeGamepadReg2["AvailableButtons"] = 384] = "AvailableButtons"; return ArcadeGamepadReg2; })(ArcadeGamepadReg || {}); var ArcadeGamepadRegPack; ((ArcadeGamepadRegPack2) => { ArcadeGamepadRegPack2.Buttons = "r: u8 u0.8"; ArcadeGamepadRegPack2.AvailableButtons = "r: u8"; })(ArcadeGamepadRegPack || (ArcadeGamepadRegPack = {})); var ArcadeGamepadEvent = /* @__PURE__ */ ((ArcadeGamepadEvent2) => { ArcadeGamepadEvent2[ArcadeGamepadEvent2["Down"] = 1] = "Down"; ArcadeGamepadEvent2[ArcadeGamepadEvent2["Up"] = 2] = "Up"; return ArcadeGamepadEvent2; })(ArcadeGamepadEvent || {}); var ArcadeGamepadEventPack; ((ArcadeGamepadEventPack2) => { ArcadeGamepadEventPack2.Down = "u8"; ArcadeGamepadEventPack2.Up = "u8"; })(ArcadeGamepadEventPack || (ArcadeGamepadEventPack = {})); var SRV_ARCADE_SOUND = 533083654; var ArcadeSoundCmd = /* @__PURE__ */ ((ArcadeSoundCmd2) => { ArcadeSoundCmd2[ArcadeSoundCmd2["Play"] = 128] = "Play"; return ArcadeSoundCmd2; })(ArcadeSoundCmd || {}); var ArcadeSoundCmdPack; ((ArcadeSoundCmdPack2) => { ArcadeSoundCmdPack2.Play = "b"; })(ArcadeSoundCmdPack || (ArcadeSoundCmdPack = {})); var ArcadeSoundReg = /* @__PURE__ */ ((ArcadeSoundReg2) => { ArcadeSoundReg2[ArcadeSoundReg2["SampleRate"] = 128] = "SampleRate"; ArcadeSoundReg2[ArcadeSoundReg2["BufferSize"] = 384] = "BufferSize"; ArcadeSoundReg2[ArcadeSoundReg2["BufferPending"] = 385] = "BufferPending"; return ArcadeSoundReg2; })(ArcadeSoundReg || {}); var ArcadeSoundRegPack; ((ArcadeSoundRegPack2) => { ArcadeSoundRegPack2.SampleRate = "u22.10"; ArcadeSoundRegPack2.BufferSize = "u32"; ArcadeSoundRegPack2.BufferPending = "u32"; })(ArcadeSoundRegPack || (ArcadeSoundRegPack = {})); var SRV_BARCODE_READER = 477339244; var BarcodeReaderFormat = /* @__PURE__ */ ((BarcodeReaderFormat2) => { BarcodeReaderFormat2[BarcodeReaderFormat2["Aztec"] = 1] = "Aztec"; BarcodeReaderFormat2[BarcodeReaderFormat2["Code128"] = 2] = "Code128"; BarcodeReaderFormat2[BarcodeReaderFormat2["Code39"] = 3] = "Code39"; BarcodeReaderFormat2[BarcodeReaderFormat2["Code93"] = 4] = "Code93"; BarcodeReaderFormat2[BarcodeReaderFormat2["Codabar"] = 5] = "Codabar"; BarcodeReaderFormat2[BarcodeReaderFormat2["DataMatrix"] = 6] = "DataMatrix"; BarcodeReaderFormat2[BarcodeReaderFormat2["Ean13"] = 8] = "Ean13"; BarcodeReaderFormat2[BarcodeReaderFormat2["Ean8"] = 9] = "Ean8"; BarcodeReaderFormat2[BarcodeReaderFormat2["ITF"] = 10] = "ITF"; BarcodeReaderFormat2[BarcodeReaderFormat2["Pdf417"] = 11] = "Pdf417"; BarcodeReaderFormat2[BarcodeReaderFormat2["QrCode"] = 12] = "QrCode"; BarcodeReaderFormat2[BarcodeReaderFormat2["UpcA"] = 13] = "UpcA"; BarcodeReaderFormat2[BarcodeReaderFormat2["UpcE"] = 14] = "UpcE"; return BarcodeReaderFormat2; })(BarcodeReaderFormat || {}); var BarcodeReaderReg = /* @__PURE__ */ ((BarcodeReaderReg2) => { BarcodeReaderReg2[BarcodeReaderReg2["Enabled"] = 1] = "Enabled"; BarcodeReaderReg2[BarcodeReaderReg2["Formats"] = 384] = "Formats"; return BarcodeReaderReg2; })(BarcodeReaderReg || {}); var BarcodeReaderRegPack; ((BarcodeReaderRegPack2) => { BarcodeReaderRegPack2.Enabled = "u8"; BarcodeReaderRegPack2.Formats = "r: u8"; })(BarcodeReaderRegPack || (BarcodeReaderRegPack = {})); var BarcodeReaderEvent = /* @__PURE__ */ ((BarcodeReaderEvent2) => { BarcodeReaderEvent2[BarcodeReaderEvent2["Detect"] = 1] = "Detect"; return BarcodeReaderEvent2; })(BarcodeReaderEvent || {}); var BarcodeReaderEventPack; ((BarcodeReaderEventPack2) => { BarcodeReaderEventPack2.Detect = "u8 s"; })(BarcodeReaderEventPack || (BarcodeReaderEventPack = {})); var SRV_BIT_RADIO = 449414863; var BitRadioReg = /* @__PURE__ */ ((BitRadioReg2) => { BitRadioReg2[BitRadioReg2["Enabled"] = 1] = "Enabled"; BitRadioReg2[BitRadioReg2["Group"] = 128] = "Group"; BitRadioReg2[BitRadioReg2["TransmissionPower"] = 129] = "TransmissionPower"; BitRadioReg2[BitRadioReg2["FrequencyBand"] = 130] = "FrequencyBand"; return BitRadioReg2; })(BitRadioReg || {}); var BitRadioRegPack; ((BitRadioRegPack2) => { BitRadioRegPack2.Enabled = "u8"; BitRadioRegPack2.Group = "u8"; BitRadioRegPack2.TransmissionPower = "u8"; BitRadioRegPack2.FrequencyBand = "u8"; })(BitRadioRegPack || (BitRadioRegPack = {})); var BitRadioCmd = /* @__PURE__ */ ((BitRadioCmd2) => { BitRadioCmd2[BitRadioCmd2["SendString"] = 128] = "SendString"; BitRadioCmd2[BitRadioCmd2["SendNumber"] = 129] = "SendNumber"; BitRadioCmd2[BitRadioCmd2["SendValue"] = 130] = "SendValue"; BitRadioCmd2[BitRadioCmd2["SendBuffer"] = 131] = "SendBuffer"; BitRadioCmd2[BitRadioCmd2["StringReceived"] = 144] = "StringReceived"; BitRadioCmd2[BitRadioCmd2["NumberReceived"] = 145] = "NumberReceived"; BitRadioCmd2[BitRadioCmd2["BufferReceived"] = 146] = "BufferReceived"; return BitRadioCmd2; })(BitRadioCmd || {}); var BitRadioCmdPack; ((BitRadioCmdPack2) => { BitRadioCmdPack2.SendString = "s"; BitRadioCmdPack2.SendNumber = "f64"; BitRadioCmdPack2.SendValue = "f64 s"; BitRadioCmdPack2.SendBuffer = "b"; BitRadioCmdPack2.StringReceived = "u32 u32 i8 b[1] s"; BitRadioCmdPack2.NumberReceived = "u32 u32 i8 b[3] f64 s"; BitRadioCmdPack2.BufferReceived = "u32 u32 i8 b[1] b"; })(BitRadioCmdPack || (BitRadioCmdPack = {})); var SRV_BOOTLOADER = 536516936; var BootloaderError = /* @__PURE__ */ ((BootloaderError2) => { BootloaderError2[BootloaderError2["NoError"] = 0] = "NoError"; BootloaderError2[BootloaderError2["PacketTooSmall"] = 1] = "PacketTooSmall"; BootloaderError2[BootloaderError2["OutOfFlashableRange"] = 2] = "OutOfFlashableRange"; BootloaderError2[BootloaderError2["InvalidPageOffset"] = 3] = "InvalidPageOffset"; BootloaderError2[BootloaderError2["NotPageAligned"] = 4] = "NotPageAligned"; return BootloaderError2; })(BootloaderError || {}); var BootloaderCmd = /* @__PURE__ */ ((BootloaderCmd2) => { BootloaderCmd2[BootloaderCmd2["Info"] = 0] = "Info"; BootloaderCmd2[BootloaderCmd2["SetSession"] = 129] = "SetSession"; BootloaderCmd2[BootloaderCmd2["PageData"] = 128] = "PageData"; return BootloaderCmd2; })(BootloaderCmd || {}); var BootloaderCmdPack; ((BootloaderCmdPack2) => { BootloaderCmdPack2.InfoReport = "u32 u32 u32 u32"; BootloaderCmdPack2.SetSession = "u32"; BootloaderCmdPack2.SetSessionReport = "u32"; BootloaderCmdPack2.PageData = "u32 u16 u8 u8 u32 u32 u32 u32 u32 b[208]"; BootloaderCmdPack2.PageDataReport = "u32 u32 u32"; })(BootloaderCmdPack || (BootloaderCmdPack = {})); var SRV_BRAILLE_DISPLAY = 331331532; var BrailleDisplayReg = /* @__PURE__ */ ((BrailleDisplayReg2) => { BrailleDisplayReg2[BrailleDisplayReg2["Enabled"] = 1] = "Enabled"; BrailleDisplayReg2[BrailleDisplayReg2["Patterns"] = 2] = "Patterns"; BrailleDisplayReg2[BrailleDisplayReg2["Length"] = 385] = "Length"; return BrailleDisplayReg2; })(BrailleDisplayReg || {}); var BrailleDisplayRegPack; ((BrailleDisplayRegPack2) => { BrailleDisplayRegPack2.Enabled = "u8"; BrailleDisplayRegPack2.Patterns = "s"; BrailleDisplayRegPack2.Length = "u8"; })(BrailleDisplayRegPack || (BrailleDisplayRegPack = {})); var SRV_BRIDGE = 535147631; var BridgeReg = /* @__PURE__ */ ((BridgeReg2) => { BridgeReg2[BridgeReg2["Enabled"] = 1] = "Enabled"; return BridgeReg2; })(BridgeReg || {}); var BridgeRegPack; ((BridgeRegPack2) => { BridgeRegPack2.Enabled = "u8"; })(BridgeRegPack || (BridgeRegPack = {})); var SRV_BUTTON = 343122531; var ButtonReg = /* @__PURE__ */ ((ButtonReg2) => { ButtonReg2[ButtonReg2["Pressure"] = 257] = "Pressure"; ButtonReg2[ButtonReg2["Analog"] = 384] = "Analog"; ButtonReg2[ButtonReg2["Pressed"] = 385] = "Pressed"; return ButtonReg2; })(ButtonReg || {}); var ButtonRegPack; ((ButtonRegPack2) => { ButtonRegPack2.Pressure = "u0.16"; ButtonRegPack2.Analog = "u8"; ButtonRegPack2.Pressed = "u8"; })(ButtonRegPack || (ButtonRegPack = {})); var ButtonEvent = /* @__PURE__ */ ((ButtonEvent2) => { ButtonEvent2[ButtonEvent2["Down"] = 1] = "Down"; ButtonEvent2[ButtonEvent2["Up"] = 2] = "Up"; ButtonEvent2[ButtonEvent2["Hold"] = 129] = "Hold"; return ButtonEvent2; })(ButtonEvent || {}); var ButtonEventPack; ((ButtonEventPack2) => { ButtonEventPack2.Up = "u32"; ButtonEventPack2.Hold = "u32"; })(ButtonEventPack || (ButtonEventPack = {})); var SRV_BUZZER = 458731991; var BuzzerReg = /* @__PURE__ */ ((BuzzerReg2) => { BuzzerReg2[BuzzerReg2["Volume"] = 1] = "Volume"; return BuzzerReg2; })(BuzzerReg || {}); var BuzzerRegPack; ((BuzzerRegPack2) => { BuzzerRegPack2.Volume = "u0.8"; })(BuzzerRegPack || (BuzzerRegPack = {})); var BuzzerCmd = /* @__PURE__ */ ((BuzzerCmd2) => { BuzzerCmd2[BuzzerCmd2["PlayTone"] = 128] = "PlayTone"; BuzzerCmd2[BuzzerCmd2["PlayNote"] = 129] = "PlayNote"; return BuzzerCmd2; })(BuzzerCmd || {}); var BuzzerCmdPack; ((BuzzerCmdPack2) => { BuzzerCmdPack2.PlayTone = "u16 u16 u16"; BuzzerCmdPack2.PlayNote = "u16 u0.16 u16"; })(BuzzerCmdPack || (BuzzerCmdPack = {})); var SRV_CAPACITIVE_BUTTON = 677752265; var CapacitiveButtonReg = /* @__PURE__ */ ((CapacitiveButtonReg2) => { CapacitiveButtonReg2[CapacitiveButtonReg2["Threshold"] = 6] = "Threshold"; return CapacitiveButtonReg2; })(CapacitiveButtonReg || {}); var CapacitiveButtonRegPack; ((CapacitiveButtonRegPack2) => { CapacitiveButtonRegPack2.Threshold = "u0.16"; })(CapacitiveButtonRegPack || (CapacitiveButtonRegPack = {})); var CapacitiveButtonCmd = /* @__PURE__ */ ((CapacitiveButtonCmd2) => { CapacitiveButtonCmd2[CapacitiveButtonCmd2["Calibrate"] = 2] = "Calibrate"; return CapacitiveButtonCmd2; })(CapacitiveButtonCmd || {}); var SRV_CHARACTER_SCREEN = 523748714; var CharacterScreenVariant = /* @__PURE__ */ ((CharacterScreenVariant2) => { CharacterScreenVariant2[CharacterScreenVariant2["LCD"] = 1] = "LCD"; CharacterScreenVariant2[CharacterScreenVariant2["OLED"] = 2] = "OLED"; CharacterScreenVariant2[CharacterScreenVariant2["Braille"] = 3] = "Braille"; return CharacterScreenVariant2; })(CharacterScreenVariant || {}); var CharacterScreenTextDirection = /* @__PURE__ */ ((CharacterScreenTextDirection2) => { CharacterScreenTextDirection2[CharacterScreenTextDirection2["LeftToRight"] = 1] = "LeftToRight"; CharacterScreenTextDirection2[CharacterScreenTextDirection2["RightToLeft"] = 2] = "RightToLeft"; return CharacterScreenTextDirection2; })(CharacterScreenTextDirection || {}); var CharacterScreenReg = /* @__PURE__ */ ((CharacterScreenReg2) => { CharacterScreenReg2[CharacterScreenReg2["Message"] = 2] = "Message"; CharacterScreenReg2[CharacterScreenReg2["Brightness"] = 1] = "Brightness"; CharacterScreenReg2[CharacterScreenReg2["Variant"] = 263] = "Variant"; CharacterScreenReg2[CharacterScreenReg2["TextDirection"] = 130] = "TextDirection"; CharacterScreenReg2[CharacterScreenReg2["Rows"] = 384] = "Rows"; CharacterScreenReg2[CharacterScreenReg2["Columns"] = 385] = "Columns"; return CharacterScreenReg2; })(CharacterScreenReg || {}); var CharacterScreenRegPack; ((CharacterScreenRegPack2) => { CharacterScreenRegPack2.Message = "s"; CharacterScreenRegPack2.Brightness = "u0.16"; CharacterScreenRegPack2.Variant = "u8"; CharacterScreenRegPack2.TextDirection = "u8"; CharacterScreenRegPack2.Rows = "u8"; CharacterScreenRegPack2.Columns = "u8"; })(CharacterScreenRegPack || (CharacterScreenRegPack = {})); var SRV_CLOUD_ADAPTER = 341864092; var CloudAdapterCmd = /* @__PURE__ */ ((CloudAdapterCmd2) => { CloudAdapterCmd2[CloudAdapterCmd2["UploadJson"] = 128] = "UploadJson"; CloudAdapterCmd2[CloudAdapterCmd2["UploadBinary"] = 129] = "UploadBinary"; return CloudAdapterCmd2; })(CloudAdapterCmd || {}); var CloudAdapterCmdPack; ((CloudAdapterCmdPack2) => { CloudAdapterCmdPack2.UploadJson = "z s"; CloudAdapterCmdPack2.UploadBinary = "z b"; })(CloudAdapterCmdPack || (CloudAdapterCmdPack = {})); var CloudAdapterReg = /* @__PURE__ */ ((CloudAdapterReg2) => { CloudAdapterReg2[CloudAdapterReg2["Connected"] = 384] = "Connected"; CloudAdapterReg2[CloudAdapterReg2["ConnectionName"] = 385] = "ConnectionName"; return CloudAdapterReg2; })(CloudAdapterReg || {}); var CloudAdapterRegPack; ((CloudAdapterRegPack2) => { CloudAdapterRegPack2.Connected = "u8"; CloudAdapterRegPack2.ConnectionName = "s"; })(CloudAdapterRegPack || (CloudAdapterRegPack = {})); var CloudAdapterEvent = /* @__PURE__ */ ((CloudAdapterEvent2) => { CloudAdapterEvent2[CloudAdapterEvent2["OnJson"] = 128] = "OnJson"; CloudAdapterEvent2[CloudAdapterEvent2["OnBinary"] = 129] = "OnBinary"; CloudAdapterEvent2[CloudAdapterEvent2["Change"] = 3] = "Change"; return CloudAdapterEvent2; })(CloudAdapterEvent || {}); var CloudAdapterEventPack; ((CloudAdapterEventPack2) => { CloudAdapterEventPack2.OnJson = "z s"; CloudAdapterEventPack2.OnBinary = "z b"; })(CloudAdapterEventPack || (CloudAdapterEventPack = {})); var SRV_CLOUD_CONFIGURATION = 342028028; var CloudConfigurationConnectionStatus = /* @__PURE__ */ ((CloudConfigurationConnectionStatus2) => { CloudConfigurationConnectionStatus2[CloudConfigurationConnectionStatus2["Connected"] = 1] = "Connected"; CloudConfigurationConnectionStatus2[CloudConfigurationConnectionStatus2["Disconnected"] = 2] = "Disconnected"; CloudConfigurationConnectionStatus2[CloudConfigurationConnectionStatus2["Connecting"] = 3] = "Connecting"; CloudConfigurationConnectionStatus2[CloudConfigurationConnectionStatus2["Disconnecting"] = 4] = "Disconnecting"; return CloudConfigurationConnectionStatus2; })(CloudConfigurationConnectionStatus || {}); var CloudConfigurationReg = /* @__PURE__ */ ((CloudConfigurationReg2) => { CloudConfigurationReg2[CloudConfigurationReg2["ServerName"] = 384] = "ServerName"; CloudConfigurationReg2[CloudConfigurationReg2["CloudDeviceId"] = 385] = "CloudDeviceId"; CloudConfigurationReg2[CloudConfigurationReg2["CloudType"] = 387] = "CloudType"; CloudConfigurationReg2[CloudConfigurationReg2["ConnectionStatus"] = 386] = "ConnectionStatus"; CloudConfigurationReg2[CloudConfigurationReg2["PushPeriod"] = 128] = "PushPeriod"; CloudConfigurationReg2[CloudConfigurationReg2["PushWatchdogPeriod"] = 129] = "PushWatchdogPeriod"; return CloudConfigurationReg2; })(CloudConfigurationReg || {}); var CloudConfigurationRegPack; ((CloudConfigurationRegPack2) => { CloudConfigurationRegPack2.ServerName = "s"; CloudConfigurationRegPack2.CloudDeviceId = "s"; CloudConfigurationRegPack2.CloudType = "s"; CloudConfigurationRegPack2.ConnectionStatus = "u16"; CloudConfigurationRegPack2.PushPeriod = "u32"; CloudConfigurationRegPack2.PushWatchdogPeriod = "u32"; })(CloudConfigurationRegPack || (CloudConfigurationRegPack = {})); var CloudConfigurationCmd = /* @__PURE__ */ ((CloudConfigurationCmd2) => { CloudConfigurationCmd2[CloudConfigurationCmd2["Connect"] = 129] = "Connect"; CloudConfigurationCmd2[CloudConfigurationCmd2["Disconnect"] = 130] = "Disconnect"; CloudConfigurationCmd2[CloudConfigurationCmd2["SetConnectionString"] = 134] = "SetConnectionString"; return CloudConfigurationCmd2; })(CloudConfigurationCmd || {}); var CloudConfigurationCmdPack; ((CloudConfigurationCmdPack2) => { CloudConfigurationCmdPack2.SetConnectionString = "s"; })(CloudConfigurationCmdPack || (CloudConfigurationCmdPack = {})); var CloudConfigurationEvent = /* @__PURE__ */ ((CloudConfigurationEvent2) => { CloudConfigurationEvent2[CloudConfigurationEvent2["ConnectionStatusChange"] = 3] = "ConnectionStatusChange"; CloudConfigurationEvent2[CloudConfigurationEvent2["MessageSent"] = 128] = "MessageSent"; return CloudConfigurationEvent2; })(CloudConfigurationEvent || {}); var CloudConfigurationEventPack; ((CloudConfigurationEventPack2) => { CloudConfigurationEventPack2.ConnectionStatusChange = "u16"; })(CloudConfigurationEventPack || (CloudConfigurationEventPack = {})); var SRV_CODAL_MESSAGE_BUS = 304085021; var CodalMessageBusCmd = /* @__PURE__ */ ((CodalMessageBusCmd2) => { CodalMessageBusCmd2[CodalMessageBusCmd2["Send"] = 128] = "Send"; return CodalMessageBusCmd2; })(CodalMessageBusCmd || {}); var CodalMessageBusCmdPack; ((CodalMessageBusCmdPack2) => { CodalMessageBusCmdPack2.Send = "u16 u16"; })(CodalMessageBusCmdPack || (CodalMessageBusCmdPack = {})); var CodalMessageBusEvent = /* @__PURE__ */ ((CodalMessageBusEvent2) => { CodalMessageBusEvent2[CodalMessageBusEvent2["Message"] = 128] = "Message"; return CodalMessageBusEvent2; })(CodalMessageBusEvent || {}); var CodalMessageBusEventPack; ((CodalMessageBusEventPack2) => { CodalMessageBusEventPack2.Message = "u16 u16"; })(CodalMessageBusEventPack || (CodalMessageBusEventPack = {})); var SRV_COLOR = 372299111; var ColorReg = /* @__PURE__ */ ((ColorReg2) => { ColorReg2[ColorReg2["Color"] = 257] = "Color"; return ColorReg2; })(ColorReg || {}); var ColorRegPack; ((ColorRegPack2) => { ColorRegPack2.Color = "u0.16 u0.16 u0.16"; })(ColorRegPack || (ColorRegPack = {})); var SRV_COMPASS = 364362175; var CompassReg = /* @__PURE__ */ ((CompassReg2) => { CompassReg2[CompassReg2["Heading"] = 257] = "Heading"; CompassReg2[CompassReg2["Enabled"] = 1] = "Enabled"; CompassReg2[CompassReg2["HeadingError"] = 262] = "HeadingError"; return CompassReg2; })(CompassReg || {}); var CompassRegPack; ((CompassRegPack2) => { CompassRegPack2.Heading = "u16.16"; CompassRegPack2.Enabled = "u8"; CompassRegPack2.HeadingError = "u16.16"; })(CompassRegPack || (CompassRegPack = {})); var CompassCmd = /* @__PURE__ */ ((CompassCmd2) => { CompassCmd2[CompassCmd2["Calibrate"] = 2] = "Calibrate"; return CompassCmd2; })(CompassCmd || {}); var SRV_CONTROL = 0; var ControlAnnounceFlags = /* @__PURE__ */ ((ControlAnnounceFlags2) => { ControlAnnounceFlags2[ControlAnnounceFlags2["RestartCounterSteady"] = 15] = "RestartCounterSteady"; ControlAnnounceFlags2[ControlAnnounceFlags2["RestartCounter1"] = 1] = "RestartCounter1"; ControlAnnounceFlags2[ControlAnnounceFlags2["RestartCounter2"] = 2] = "RestartCounter2"; ControlAnnounceFlags2[ControlAnnounceFlags2["RestartCounter4"] = 4] = "RestartCounter4"; ControlAnnounceFlags2[ControlAnnounceFlags2["RestartCounter8"] = 8] = "RestartCounter8"; ControlAnnounceFlags2[ControlAnnounceFlags2["StatusLightNone"] = 0] = "StatusLightNone"; ControlAnnounceFlags2[ControlAnnounceFlags2["StatusLightMono"] = 16] = "StatusLightMono"; ControlAnnounceFlags2[ControlAnnounceFlags2["StatusLightRgbNoFade"] = 32] = "StatusLightRgbNoFade"; ControlAnnounceFlags2[ControlAnnounceFlags2["StatusLightRgbFade"] = 48] = "StatusLightRgbFade"; ControlAnnounceFlags2[ControlAnnounceFlags2["SupportsACK"] = 256] = "SupportsACK"; ControlAnnounceFlags2[ControlAnnounceFlags2["SupportsBroadcast"] = 512] = "SupportsBroadcast"; ControlAnnounceFlags2[ControlAnnounceFlags2["SupportsFrames"] = 1024] = "SupportsFrames"; ControlAnnounceFlags2[ControlAnnounceFlags2["IsClient"] = 2048] = "IsClient"; ControlAnnounceFlags2[ControlAnnounceFlags2["SupportsReliableCommands"] = 4096] = "SupportsReliableCommands"; return ControlAnnounceFlags2; })(ControlAnnounceFlags || {}); var ControlCmd = /* @__PURE__ */ ((ControlCmd2) => { ControlCmd2[ControlCmd2["Services"] = 0] = "Services"; ControlCmd2[ControlCmd2["Noop"] = 128] = "Noop"; ControlCmd2[ControlCmd2["Identify"] = 129] = "Identify"; ControlCmd2[ControlCmd2["Reset"] = 130] = "Reset"; ControlCmd2[ControlCmd2["FloodPing"] = 131] = "FloodPing"; ControlCmd2[ControlCmd2["SetStatusLight"] = 132] = "SetStatusLight"; ControlCmd2[ControlCmd2["Proxy"] = 133] = "Proxy"; ControlCmd2[ControlCmd2["ReliableCommands"] = 134] = "ReliableCommands"; ControlCmd2[ControlCmd2["Standby"] = 135] = "Standby"; return ControlCmd2; })(ControlCmd || {}); var ControlCmdPack; ((ControlCmdPack2) => { ControlCmdPack2.ServicesReport = "u16 u8 u8 r: u32"; ControlCmdPack2.FloodPing = "u32 u32 u8"; ControlCmdPack2.FloodPingReport = "u32 b"; ControlCmdPack2.SetStatusLight = "u8 u8 u8 u8"; ControlCmdPack2.ReliableCommands = "u32"; ControlCmdPack2.ReliableCommandsReport = "b[12]"; ControlCmdPack2.Standby = "u32"; })(ControlCmdPack || (ControlCmdPack = {})); var ControlPipe = /* @__PURE__ */ ((ControlPipe2) => { })(ControlPipe || {}); var ControlPipePack; ((ControlPipePack2) => { ControlPipePack2.WrappedCommand = "u8 u8 u16 b"; })(ControlPipePack || (ControlPipePack = {})); var ControlReg = /* @__PURE__ */ ((ControlReg2) => { ControlReg2[ControlReg2["ResetIn"] = 128] = "ResetIn"; ControlReg2[ControlReg2["DeviceDescription"] = 384] = "DeviceDescription"; ControlReg2[ControlReg2["ProductIdentifier"] = 385] = "ProductIdentifier"; ControlReg2[ControlReg2["BootloaderProductIdentifier"] = 388] = "BootloaderProductIdentifier"; ControlReg2[ControlReg2["FirmwareVersion"] = 389] = "FirmwareVersion"; ControlReg2[ControlReg2["McuTemperature"] = 386] = "McuTemperature"; ControlReg2[ControlReg2["Uptime"] = 390] = "Uptime"; return ControlReg2; })(ControlReg || {}); var ControlRegPack; ((ControlRegPack2) => { ControlRegPack2.ResetIn = "u32"; ControlRegPack2.DeviceDescription = "s"; ControlRegPack2.ProductIdentifier = "u32"; ControlRegPack2.BootloaderProductIdentifier = "u32"; ControlRegPack2.FirmwareVersion = "s"; ControlRegPack2.McuTemperature = "i16"; ControlRegPack2.Uptime = "u64"; })(ControlRegPack || (ControlRegPack = {})); var SRV_DASHBOARD = 468029703; var SRV_DC_CURRENT_MEASUREMENT = 420661422; var DcCurrentMeasurementReg = /* @__PURE__ */ ((DcCurrentMeasurementReg2) => { DcCurrentMeasurementReg2[DcCurrentMeasurementReg2["MeasurementName"] = 386] = "MeasurementName"; DcCurrentMeasurementReg2[DcCurrentMeasurementReg2["Measurement"] = 257] = "Measurement"; DcCurrentMeasurementReg2[DcCurrentMeasurementReg2["MeasurementError"] = 262] = "MeasurementError"; DcCurrentMeasurementReg2[DcCurrentMeasurementReg2["MinMeasurement"] = 260] = "MinMeasurement"; DcCurrentMeasurementReg2[DcCurrentMeasurementReg2["MaxMeasurement"] = 261] = "MaxMeasurement"; return DcCurrentMeasurementReg2; })(DcCurrentMeasurementReg || {}); var DcCurrentMeasurementRegPack; ((DcCurrentMeasurementRegPack2) => { DcCurrentMeasurementRegPack2.MeasurementName = "s"; DcCurrentMeasurementRegPack2.Measurement = "f64"; DcCurrentMeasurementRegPack2.MeasurementError = "f64"; DcCurrentMeasurementRegPack2.MinMeasurement = "f64"; DcCurrentMeasurementRegPack2.MaxMeasurement = "f64"; })(DcCurrentMeasurementRegPack || (DcCurrentMeasurementRegPack = {})); var SRV_DC_VOLTAGE_MEASUREMENT = 372485145; var DcVoltageMeasurementVoltageMeasurementType = /* @__PURE__ */ ((DcVoltageMeasurementVoltageMeasurementType2) => { DcVoltageMeasurementVoltageMeasurementType2[DcVoltageMeasurementVoltageMeasurementType2["Absolute"] = 0] = "Absolute"; DcVoltageMeasurementVoltageMeasurementType2[DcVoltageMeasurementVoltageMeasurementType2["Differential"] = 1] = "Differential"; return DcVoltageMeasurementVoltageMeasurementType2; })(DcVoltageMeasurementVoltageMeasurementType || {}); var DcVoltageMeasurementReg = /* @__PURE__ */ ((DcVoltageMeasurementReg2) => { DcVoltageMeasurementReg2[DcVoltageMeasurementReg2["MeasurementType"] = 385] = "MeasurementType"; DcVoltageMeasurementReg2[DcVoltageMeasurementReg2["MeasurementName"] = 386] = "MeasurementName"; DcVoltageMeasurementReg2[DcVoltageMeasurementReg2["Measurement"] = 257] = "Measurement"; DcVoltageMeasurementReg2[DcVoltageMeasurementReg2["MeasurementError"] = 262] = "MeasurementError"; DcVoltageMeasurementReg2[DcVoltageMeasurementReg2["MinMeasurement"] = 260] = "MinMeasurement"; DcVoltageMeasurementReg2[DcVoltageMeasurementReg2["MaxMeasurement"] = 261] = "MaxMeasurement"; return DcVoltageMeasurementReg2; })(DcVoltageMeasurementReg || {}); var DcVoltageMeasurementRegPack; ((DcVoltageMeasurementRegPack2) => { DcVoltageMeasurementRegPack2.MeasurementType = "u8"; DcVoltageMeasurementRegPack2.MeasurementName = "s"; DcVoltageMeasurementRegPack2.Measurement = "f64"; DcVoltageMeasurementRegPack2.MeasurementError = "f64"; DcVoltageMeasurementRegPack2.MinMeasurement = "f64"; DcVoltageMeasurementRegPack2.MaxMeasurement = "f64"; })(DcVoltageMeasurementRegPack || (DcVoltageMeasurementRegPack = {})); var SRV_DEVICE_SCRIPT_CONDITION = 295074157; var DeviceScriptConditionCmd = /* @__PURE__ */ ((DeviceScriptConditionCmd2) => { DeviceScriptConditionCmd2[DeviceScriptConditionCmd2["Signal"] = 128] = "Signal"; return DeviceScriptConditionCmd2; })(DeviceScriptConditionCmd || {}); var DeviceScriptConditionEvent = /* @__PURE__ */ ((DeviceScriptConditionEvent2) => { DeviceScriptConditionEvent2[DeviceScriptConditionEvent2["Signalled"] = 3] = "Signalled"; return DeviceScriptConditionEvent2; })(DeviceScriptConditionEvent || {}); var SRV_DEVS_DBG = 358308672; var DevsDbgValueTag = /* @__PURE__ */ ((DevsDbgValueTag2) => { DevsDbgValueTag2[DevsDbgValueTag2["Number"] = 1] = "Number"; DevsDbgValueTag2[DevsDbgValueTag2["Special"] = 2] = "Special"; DevsDbgValueTag2[DevsDbgValueTag2["Fiber"] = 3] = "Fiber"; DevsDbgValueTag2[DevsDbgValueTag2["BuiltinObject"] = 5] = "BuiltinObject"; DevsDbgValueTag2[DevsDbgValueTag2["Exotic"] = 6] = "Exotic"; DevsDbgValueTag2[DevsDbgValueTag2["Unhandled"] = 7] = "Unhandled"; DevsDbgValueTag2[DevsDbgValueTag2["ImgBuffer"] = 32] = "ImgBuffer"; DevsDbgValueTag2[DevsDbgValueTag2["ImgStringBuiltin"] = 33] = "ImgStringBuiltin"; DevsDbgValueTag2[DevsDbgValueTag2["ImgStringAscii"] = 34] = "ImgStringAscii"; DevsDbgValueTag2[DevsDbgValueTag2["ImgStringUTF8"] = 35] = "ImgStringUTF8"; DevsDbgValueTag2[DevsDbgValueTag2["ImgRole"] = 48] = "ImgRole"; DevsDbgValueTag2[DevsDbgValueTag2["ImgFunction"] = 49] = "ImgFunction"; DevsDbgValueTag2[DevsDbgValueTag2["ImgRoleMember"] = 50] = "ImgRoleMember"; DevsDbgValueTag2[DevsDbgValueTag2["ObjArray"] = 81] = "ObjArray"; DevsDbgValueTag2[DevsDbgValueTag2["ObjMap"] = 82] = "ObjMap"; DevsDbgValueTag2[DevsDbgValueTag2["ObjBuffer"] = 83] = "ObjBuffer"; DevsDbgValueTag2[DevsDbgValueTag2["ObjString"] = 84] = "ObjString"; DevsDbgValueTag2[DevsDbgValueTag2["ObjStackFrame"] = 85] = "ObjStackFrame"; DevsDbgValueTag2[DevsDbgValueTag2["ObjPacket"] = 86] = "ObjPacket"; DevsDbgValueTag2[DevsDbgValueTag2["ObjBoundFunction"] = 87] = "ObjBoundFunction"; DevsDbgValueTag2[DevsDbgValueTag2["ObjOpaque"] = 88] = "ObjOpaque"; DevsDbgValueTag2[DevsDbgValueTag2["ObjAny"] = 80] = "ObjAny"; DevsDbgValueTag2[DevsDbgValueTag2["ObjMask"] = 240] = "ObjMask"; DevsDbgValueTag2[DevsDbgValueTag2["User1"] = 241] = "User1"; DevsDbgValueTag2[DevsDbgValueTag2["User2"] = 242] = "User2"; DevsDbgValueTag2[DevsDbgValueTag2["User3"] = 243] = "User3"; DevsDbgValueTag2[DevsDbgValueTag2["User4"] = 244] = "User4"; return DevsDbgValueTag2; })(DevsDbgValueTag || {}); var DevsDbgValueSpecial = /* @__PURE__ */ ((DevsDbgValueSpecial2) => { DevsDbgValueSpecial2[DevsDbgValueSpecial2["Undefined"] = 0] = "Undefined"; DevsDbgValueSpecial2[DevsDbgValueSpecial2["True"] = 1] = "True"; DevsDbgValueSpecial2[DevsDbgValueSpecial2["False"] = 2] = "False"; DevsDbgValueSpecial2[DevsDbgValueSpecial2["Null"] = 3] = "Null"; DevsDbgValueSpecial2[DevsDbgValueSpecial2["Globals"] = 100] = "Globals"; DevsDbgValueSpecial2[DevsDbgValueSpecial2["CurrentException"] = 101] = "CurrentException"; return DevsDbgValueSpecial2; })(DevsDbgValueSpecial || {}); var DevsDbgFunIdx = /* @__PURE__ */ ((DevsDbgFunIdx2) => { DevsDbgFunIdx2[DevsDbgFunIdx2["None"] = 0] = "None"; DevsDbgFunIdx2[DevsDbgFunIdx2["Main"] = 49999] = "Main"; DevsDbgFunIdx2[DevsDbgFunIdx2["FirstBuiltIn"] = 5e4] = "FirstBuiltIn"; return DevsDbgFunIdx2; })(DevsDbgFunIdx || {}); var DevsDbgFiberHandle = /* @__PURE__ */ ((DevsDbgFiberHandle2) => { DevsDbgFiberHandle2[DevsDbgFiberHandle2["None"] = 0] = "None"; return DevsDbgFiberHandle2; })(DevsDbgFiberHandle || {}); var DevsDbgProgramCounter = /* @__PURE__ */ ((DevsDbgProgramCounter2) => { })(DevsDbgProgramCounter || {}); var DevsDbgObjStackFrame = /* @__PURE__ */ ((DevsDbgObjStackFrame2) => { DevsDbgObjStackFrame2[DevsDbgObjStackFrame2["Null"] = 0] = "Null"; return DevsDbgObjStackFrame2; })(DevsDbgObjStackFrame || {}); var DevsDbgString = /* @__PURE__ */ ((DevsDbgString2) => { DevsDbgString2[DevsDbgString2["StaticIndicatorMask"] = 2147483649] = "StaticIndicatorMask"; DevsDbgString2[DevsDbgString2["StaticTagMask"] = 2130706432] = "StaticTagMask"; DevsDbgString2[DevsDbgString2["StaticIndexMask"] = 16777214] = "StaticIndexMask"; DevsDbgString2[DevsDbgString2["Unhandled"] = 0] = "Unhandled"; return DevsDbgString2; })(DevsDbgString || {}); var DevsDbgStepFlags = /* @__PURE__ */ ((DevsDbgStepFlags2) => { DevsDbgStepFlags2[DevsDbgStepFlags2["StepOut"] = 1] = "StepOut"; DevsDbgStepFlags2[DevsDbgStepFlags2["StepIn"] = 2] = "StepIn"; DevsDbgStepFlags2[DevsDbgStepFlags2["Throw"] = 4] = "Throw"; return DevsDbgStepFlags2; })(DevsDbgStepFlags || {}); var DevsDbgSuspensionType = /* @__PURE__ */ ((DevsDbgSuspensionType2) => { DevsDbgSuspensionType2[DevsDbgSuspensionType2["None"] = 0] = "None"; DevsDbgSuspensionType2[DevsDbgSuspensionType2["Breakpoint"] = 1] = "Breakpoint"; DevsDbgSuspensionType2[DevsDbgSuspensionType2["UnhandledException"] = 2] = "UnhandledException"; DevsDbgSuspensionType2[DevsDbgSuspensionType2["HandledException"] = 3] = "HandledException"; DevsDbgSuspensionType2[DevsDbgSuspensionType2["Halt"] = 4] = "Halt"; DevsDbgSuspensionType2[DevsDbgSuspensionType2["Panic"] = 5] = "Panic"; DevsDbgSuspensionType2[DevsDbgSuspensionType2["Restart"] = 6] = "Restart"; DevsDbgSuspensionType2[DevsDbgSuspensionType2["DebuggerStmt"] = 7] = "DebuggerStmt"; DevsDbgSuspensionType2[DevsDbgSuspensionType2["Step"] = 8] = "Step"; return DevsDbgSuspensionType2; })(DevsDbgSuspensionType || {}); var DevsDbgCmd = /* @__PURE__ */ ((DevsDbgCmd2) => { DevsDbgCmd2[DevsDbgCmd2["ReadFibers"] = 128] = "ReadFibers"; DevsDbgCmd2[DevsDbgCmd2["ReadStack"] = 129] = "ReadStack"; DevsDbgCmd2[DevsDbgCmd2["ReadIndexedValues"] = 130] = "ReadIndexedValues"; DevsDbgCmd2[DevsDbgCmd2["ReadNamedValues"] = 131] = "ReadNamedValues"; DevsDbgCmd2[DevsDbgCmd2["ReadValue"] = 132] = "ReadValue"; DevsDbgCmd2[DevsDbgCmd2["ReadBytes"] = 133] = "ReadBytes"; DevsDbgCmd2[DevsDbgCmd2["SetBreakpoints"] = 144] = "SetBreakpoints"; DevsDbgCmd2[DevsDbgCmd2["ClearBreakpoints"] = 145] = "ClearBreakpoints"; DevsDbgCmd2[DevsDbgCmd2["ClearAllBreakpoints"] = 146] = "ClearAllBreakpoints"; DevsDbgCmd2[DevsDbgCmd2["Resume"] = 147] = "Resume"; DevsDbgCmd2[DevsDbgCmd2["Halt"] = 148] = "Halt"; DevsDbgCmd2[DevsDbgCmd2["RestartAndHalt"] = 149] = "RestartAndHalt"; DevsDbgCmd2[DevsDbgCmd2["Step"] = 150] = "Step"; return DevsDbgCmd2; })(DevsDbgCmd || {}); var DevsDbgCmdPack; ((DevsDbgCmdPack2) => { DevsDbgCmdPack2.ReadFibers = "b[12]"; DevsDbgCmdPack2.ReadStack = "b[12] u32"; DevsDbgCmdPack2.ReadIndexedValues = "b[12] u32 u8 u8 u16 u16"; DevsDbgCmdPack2.ReadNamedValues = "b[12] u32 u8"; DevsDbgCmdPack2.ReadValue = "u32 u8"; DevsDbgCmdPack2.ReadValueReport = "u32 u32 u16 u8"; DevsDbgCmdPack2.ReadBytes = "b[12] u32 u8 u8 u16 u16"; DevsDbgCmdPack2.SetBreakpoints = "r: u32"; DevsDbgCmdPack2.ClearBreakpoints = "r: u32"; DevsDbgCmdPack2.Step = "u32 u16 u16 r: u32"; })(DevsDbgCmdPack || (DevsDbgCmdPack = {})); var DevsDbgPipe = /* @__PURE__ */ ((DevsDbgPipe2) => { })(DevsDbgPipe || {}); var DevsDbgPipePack; ((DevsDbgPipePack2) => { DevsDbgPipePack2.Fiber = "u32 u16 u16"; DevsDbgPipePack2.Stackframe = "u32 u32 u32 u16 u16"; DevsDbgPipePack2.Value = "u32 u32 u16 u8"; DevsDbgPipePack2.KeyValue = "u32 u32 u32 u16 u8"; DevsDbgPipePack2.BytesValue = "b"; })(DevsDbgPipePack || (DevsDbgPipePack = {})); var DevsDbgReg = /* @__PURE__ */ ((DevsDbgReg2) => { DevsDbgReg2[DevsDbgReg2["Enabled"] = 1] = "Enabled"; DevsDbgReg2[DevsDbgReg2["BreakAtUnhandledExn"] = 128] = "BreakAtUnhandledExn"; DevsDbgReg2[DevsDbgReg2["BreakAtHandledExn"] = 129] = "BreakAtHandledExn"; DevsDbgReg2[DevsDbgReg2["IsSuspended"] = 384] = "IsSuspended"; return DevsDbgReg2; })(DevsDbgReg || {}); var DevsDbgRegPack; ((DevsDbgRegPack2) => { DevsDbgRegPack2.Enabled = "u8"; DevsDbgRegPack2.BreakAtUnhandledExn = "u8"; DevsDbgRegPack2.BreakAtHandledExn = "u8"; DevsDbgRegPack2.IsSuspended = "u8"; })(DevsDbgRegPack || (DevsDbgRegPack = {})); var DevsDbgEvent = /* @__PURE__ */ ((DevsDbgEvent2) => { DevsDbgEvent2[DevsDbgEvent2["Suspended"] = 128] = "Suspended"; return DevsDbgEvent2; })(DevsDbgEvent || {}); var DevsDbgEventPack; ((DevsDbgEventPack2) => { DevsDbgEventPack2.Suspended = "u32 u8"; })(DevsDbgEventPack || (DevsDbgEventPack = {})); var SRV_DEVICE_SCRIPT_MANAGER = 288680491; var DeviceScriptManagerCmd = /* @__PURE__ */ ((DeviceScriptManagerCmd2) => { DeviceScriptManagerCmd2[DeviceScriptManagerCmd2["DeployBytecode"] = 128] = "DeployBytecode"; DeviceScriptManagerCmd2[DeviceScriptManagerCmd2["ReadBytecode"] = 129] = "ReadBytecode"; return DeviceScriptManagerCmd2; })(DeviceScriptManagerCmd || {}); var DeviceScriptManagerCmdPack; ((DeviceScriptManagerCmdPack2) => { DeviceScriptManagerCmdPack2.DeployBytecode = "u32"; DeviceScriptManagerCmdPack2.DeployBytecodeReport = "u16"; DeviceScriptManagerCmdPack2.ReadBytecode = "b[12]"; })(DeviceScriptManagerCmdPack || (DeviceScriptManagerCmdPack = {})); var DeviceScriptManagerPipe = /* @__PURE__ */ ((DeviceScriptManagerPipe2) => { })(DeviceScriptManagerPipe || {}); var DeviceScriptManagerPipePack; ((DeviceScriptManagerPipePack2) => { DeviceScriptManagerPipePack2.Bytecode = "b"; })(DeviceScriptManagerPipePack || (DeviceScriptManagerPipePack = {})); var DeviceScriptManagerReg = /* @__PURE__ */ ((DeviceScriptManagerReg2) => { DeviceScriptManagerReg2[DeviceScriptManagerReg2["Running"] = 128] = "Running"; DeviceScriptManagerReg2[DeviceScriptManagerReg2["Autostart"] = 129] = "Autostart"; DeviceScriptManagerReg2[DeviceScriptManagerReg2["ProgramSize"] = 384] = "ProgramSize"; DeviceScriptManagerReg2[DeviceScriptManagerReg2["ProgramHash"] = 385] = "ProgramHash"; DeviceScriptManagerReg2[DeviceScriptManagerReg2["ProgramSha256"] = 386] = "ProgramSha256"; DeviceScriptManagerReg2[DeviceScriptManagerReg2["RuntimeVersion"] = 387] = "RuntimeVersion"; DeviceScriptManagerReg2[DeviceScriptManagerReg2["ProgramName"] = 388] = "ProgramName"; DeviceScriptManagerReg2[DeviceScriptManagerReg2["ProgramVersion"] = 389] = "ProgramVersion"; return DeviceScriptManagerReg2; })(DeviceScriptManagerReg || {}); var DeviceScriptManagerRegPack; ((DeviceScriptManagerRegPack2) => { DeviceScriptManagerRegPack2.Running = "u8"; DeviceScriptManagerRegPack2.Autostart = "u8"; DeviceScriptManagerRegPack2.ProgramSize = "u32"; DeviceScriptManagerRegPack2.ProgramHash = "u32"; DeviceScriptManagerRegPack2.ProgramSha256 = "b[32]"; DeviceScriptManagerRegPack2.RuntimeVersion = "u16 u8 u8"; DeviceScriptManagerRegPack2.ProgramName = "s"; DeviceScriptManagerRegPack2.ProgramVersion = "s"; })(DeviceScriptManagerRegPack || (DeviceScriptManagerRegPack = {})); var DeviceScriptManagerEvent = /* @__PURE__ */ ((DeviceScriptManagerEvent2) => { DeviceScriptManagerEvent2[DeviceScriptManagerEvent2["ProgramPanic"] = 128] = "ProgramPanic"; DeviceScriptManagerEvent2[DeviceScriptManagerEvent2["ProgramChange"] = 3] = "ProgramChange"; return DeviceScriptManagerEvent2; })(DeviceScriptManagerEvent || {}); var DeviceScriptManagerEventPack; ((DeviceScriptManagerEventPack2) => { DeviceScriptManagerEventPack2.ProgramPanic = "u32 u32"; })(DeviceScriptManagerEventPack || (DeviceScriptManagerEventPack = {})); var SRV_DISTANCE = 337275786; var DistanceVariant = /* @__PURE__ */ ((DistanceVariant2) => { DistanceVariant2[DistanceVariant2["Ultrasonic"] = 1] = "Ultrasonic"; DistanceVariant2[DistanceVariant2["Infrared"] = 2] = "Infrared"; DistanceVariant2[DistanceVariant2["LiDAR"] = 3] = "LiDAR"; DistanceVariant2[DistanceVariant2["Laser"] = 4] = "Laser"; return DistanceVariant2; })(DistanceVariant || {}); var DistanceReg = /* @__PURE__ */ ((DistanceReg2) => { DistanceReg2[DistanceReg2["Distance"] = 257] = "Distance"; DistanceReg2[DistanceReg2["DistanceError"] = 262] = "DistanceError"; DistanceReg2[DistanceReg2["MinRange"] = 260] = "MinRange"; DistanceReg2[DistanceReg2["MaxRange"] = 261] = "MaxRange"; DistanceReg2[DistanceReg2["Variant"] = 263] = "Variant"; return DistanceReg2; })(DistanceReg || {}); var DistanceRegPack; ((DistanceRegPack2) => { DistanceRegPack2.Distance = "u16.16"; DistanceRegPack2.DistanceError = "u16.16"; DistanceRegPack2.MinRange = "u16.16"; DistanceRegPack2.MaxRange = "u16.16"; DistanceRegPack2.Variant = "u8"; })(DistanceRegPack || (DistanceRegPack = {})); var SRV_DMX = 298814469; var DmxReg = /* @__PURE__ */ ((DmxReg2) => { DmxReg2[DmxReg2["Enabled"] = 1] = "Enabled"; return DmxReg2; })(DmxReg || {}); var DmxRegPack; ((DmxRegPack2) => { DmxRegPack2.Enabled = "u8"; })(DmxRegPack || (DmxRegPack = {})); var DmxCmd = /* @__PURE__ */ ((DmxCmd2) => { DmxCmd2[DmxCmd2["Send"] = 128] = "Send"; return DmxCmd2; })(DmxCmd || {}); var DmxCmdPack; ((DmxCmdPack2) => { DmxCmdPack2.Send = "b"; })(DmxCmdPack || (DmxCmdPack = {})); var SRV_DOT_MATRIX = 286070091; var DotMatrixVariant = /* @__PURE__ */ ((DotMatrixVariant2) => { DotMatrixVariant2[DotMatrixVariant2["LED"] = 1] = "LED"; DotMatrixVariant2[DotMatrixVariant2["Braille"] = 2] = "Braille"; return DotMatrixVariant2; })(DotMatrixVariant || {}); var DotMatrixReg = /* @__PURE__ */ ((DotMatrixReg2) => { DotMatrixReg2[DotMatrixReg2["Dots"] = 2] = "Dots"; DotMatrixReg2[DotMatrixReg2["Brightness"] = 1] = "Brightness"; DotMatrixReg2[DotMatrixReg2["Rows"] = 385] = "Rows"; DotMatrixReg2[DotMatrixReg2["Columns"] = 386] = "Columns"; DotMatrixReg2[DotMatrixReg2["Variant"] = 263] = "Variant"; return DotMatrixReg2; })(DotMatrixReg || {}); var DotMatrixRegPack; ((DotMatrixRegPack2) => { DotMatrixRegPack2.Dots = "b"; DotMatrixRegPack2.Brightness = "u0.8"; DotMatrixRegPack2.Rows = "u16"; DotMatrixRegPack2.Columns = "u16"; DotMatrixRegPack2.Variant = "u8"; })(DotMatrixRegPack || (DotMatrixRegPack = {})); var SRV_DUAL_MOTORS = 355063095; var DualMotorsReg = /* @__PURE__ */ ((DualMotorsReg2) => { DualMotorsReg2[DualMotorsReg2["Speed"] = 2] = "Speed"; DualMotorsReg2[DualMotorsReg2["Enabled"] = 1] = "Enabled"; DualMotorsReg2[DualMotorsReg2["LoadTorque"] = 384] = "LoadTorque"; DualMotorsReg2[DualMotorsReg2["LoadRotationSpeed"] = 385] = "LoadRotationSpeed"; DualMotorsReg2[DualMotorsReg2["Reversible"] = 386] = "Reversible"; return DualMotorsReg2; })(DualMotorsReg || {}); var DualMotorsRegPack; ((DualMotorsRegPack2) => { DualMotorsRegPack2.Speed = "i1.15 i1.15"; DualMotorsRegPack2.Enabled = "u8"; DualMotorsRegPack2.LoadTorque = "u16.16"; DualMotorsRegPack2.LoadRotationSpeed = "u16.16"; DualMotorsRegPack2.Reversible = "u8"; })(DualMotorsRegPack || (DualMotorsRegPack = {})); var SRV_E_CO2 = 379362758; var ECO2Variant = /* @__PURE__ */ ((ECO2Variant2) => { ECO2Variant2[ECO2Variant2["VOC"] = 1] = "VOC"; ECO2Variant2[ECO2Variant2["NDIR"] = 2] = "NDIR"; return ECO2Variant2; })(ECO2Variant || {}); var ECO2Reg = /* @__PURE__ */ ((ECO2Reg2) => { ECO2Reg2[ECO2Reg2["ECO2"] = 257] = "ECO2"; ECO2Reg2[ECO2Reg2["ECO2Error"] = 262] = "ECO2Error"; ECO2Reg2[ECO2Reg2["MinECO2"] = 260] = "MinECO2"; ECO2Reg2[ECO2Reg2["MaxECO2"] = 261] = "MaxECO2"; ECO2Reg2[ECO2Reg2["Variant"] = 263] = "Variant"; return ECO2Reg2; })(ECO2Reg || {}); var ECO2RegPack; ((ECO2RegPack2) => { ECO2RegPack2.ECO2 = "u22.10"; ECO2RegPack2.ECO2Error = "u22.10"; ECO2RegPack2.MinECO2 = "u22.10"; ECO2RegPack2.MaxECO2 = "u22.10"; ECO2RegPack2.Variant = "u8"; })(ECO2RegPack || (ECO2RegPack = {})); var SRV_FLEX = 524797638; var FlexReg = /* @__PURE__ */ ((FlexReg2) => { FlexReg2[FlexReg2["Bending"] = 257] = "Bending"; FlexReg2[FlexReg2["Length"] = 384] = "Length"; return FlexReg2; })(FlexReg || {}); var FlexRegPack; ((FlexRegPack2) => { FlexRegPack2.Bending = "i1.15"; FlexRegPack2.Length = "u16"; })(FlexRegPack || (FlexRegPack = {})); var SRV_GAMEPAD = 277836886; var GamepadButtons = /* @__PURE__ */ ((GamepadButtons2) => { GamepadButtons2[GamepadButtons2["Left"] = 1] = "Left"; GamepadButtons2[GamepadButtons2["Up"] = 2] = "Up"; GamepadButtons2[GamepadButtons2["Right"] = 4] = "Right"; GamepadButtons2[GamepadButtons2["Down"] = 8] = "Down"; GamepadButtons2[GamepadButtons2["A"] = 16] = "A"; GamepadButtons2[GamepadButtons2["B"] = 32] = "B"; GamepadButtons2[GamepadButtons2["Menu"] = 64] = "Menu"; GamepadButtons2[GamepadButtons2["Select"] = 128] = "Select"; GamepadButtons2[GamepadButtons2["Reset"] = 256] = "Reset"; GamepadButtons2[GamepadButtons2["Exit"] = 512] = "Exit"; GamepadButtons2[GamepadButtons2["X"] = 1024] = "X"; GamepadButtons2[GamepadButtons2["Y"] = 2048] = "Y"; return GamepadButtons2; })(GamepadButtons || {}); var GamepadVariant = /* @__PURE__ */ ((GamepadVariant2) => { GamepadVariant2[GamepadVariant2["Thumb"] = 1] = "Thumb"; GamepadVariant2[GamepadVariant2["ArcadeBall"] = 2] = "ArcadeBall"; GamepadVariant2[GamepadVariant2["ArcadeStick"] = 3] = "ArcadeStick"; GamepadVariant2[GamepadVariant2["Gamepad"] = 4] = "Gamepad"; return GamepadVariant2; })(GamepadVariant || {}); var GamepadReg = /* @__PURE__ */ ((GamepadReg2) => { GamepadReg2[GamepadReg2["Direction"] = 257] = "Direction"; GamepadReg2[GamepadReg2["Variant"] = 263] = "Variant"; GamepadReg2[GamepadReg2["ButtonsAvailable"] = 384] = "ButtonsAvailable"; return GamepadReg2; })(GamepadReg || {}); var GamepadRegPack; ((GamepadRegPack2) => { GamepadRegPack2.Direction = "u32 i1.15 i1.15"; GamepadRegPack2.Variant = "u8"; GamepadRegPack2.ButtonsAvailable = "u32"; })(GamepadRegPack || (GamepadRegPack = {})); var GamepadEvent = /* @__PURE__ */ ((GamepadEvent2) => { GamepadEvent2[GamepadEvent2["ButtonsChanged"] = 3] = "ButtonsChanged"; return GamepadEvent2; })(GamepadEvent || {}); var GamepadEventPack; ((GamepadEventPack2) => { GamepadEventPack2.ButtonsChanged = "u32"; })(GamepadEventPack || (GamepadEventPack = {})); var SRV_GPIO = 282614377; var GPIOMode = /* @__PURE__ */ ((GPIOMode2) => { GPIOMode2[GPIOMode2["Off"] = 0] = "Off"; GPIOMode2[GPIOMode2["OffPullUp"] = 16] = "OffPullUp"; GPIOMode2[GPIOMode2["OffPullDown"] = 32] = "OffPullDown"; GPIOMode2[GPIOMode2["Input"] = 1] = "Input"; GPIOMode2[GPIOMode2["InputPullUp"] = 17] = "InputPullUp"; GPIOMode2[GPIOMode2["InputPullDown"] = 33] = "InputPullDown"; GPIOMode2[GPIOMode2["Output"] = 2] = "Output"; GPIOMode2[GPIOMode2["OutputHigh"] = 18] = "OutputHigh"; GPIOMode2[GPIOMode2["OutputLow"] = 34] = "OutputLow"; GPIOMode2[GPIOMode2["AnalogIn"] = 3] = "AnalogIn"; GPIOMode2[GPIOMode2["Alternative"] = 4] = "Alternative"; GPIOMode2[GPIOMode2["BaseModeMask"] = 15] = "BaseModeMask"; return GPIOMode2; })(GPIOMode || {}); var GPIOCapabilities = /* @__PURE__ */ ((GPIOCapabilities2) => { GPIOCapabilities2[GPIOCapabilities2["PullUp"] = 1] = "PullUp"; GPIOCapabilities2[GPIOCapabilities2["PullDown"] = 2] = "PullDown"; GPIOCapabilities2[GPIOCapabilities2["Input"] = 4] = "Input"; GPIOCapabilities2[GPIOCapabilities2["Output"] = 8] = "Output"; GPIOCapabilities2[GPIOCapabilities2["Analog"] = 16] = "Analog"; return GPIOCapabilities2; })(GPIOCapabilities || {}); var GPIOReg = /* @__PURE__ */ ((GPIOReg2) => { GPIOReg2[GPIOReg2["State"] = 257] = "State"; GPIOReg2[GPIOReg2["NumPins"] = 384] = "NumPins"; return GPIOReg2; })(GPIOReg || {}); var GPIORegPack; ((GPIORegPack2) => { GPIORegPack2.State = "b"; GPIORegPack2.NumPins = "u8"; })(GPIORegPack || (GPIORegPack = {})); var GPIOCmd = /* @__PURE__ */ ((GPIOCmd2) => { GPIOCmd2[GPIOCmd2["Configure"] = 128] = "Configure"; GPIOCmd2[GPIOCmd2["PinInfo"] = 129] = "PinInfo"; GPIOCmd2[GPIOCmd2["PinByLabel"] = 131] = "PinByLabel"; GPIOCmd2[GPIOCmd2["PinByHwPin"] = 132] = "PinByHwPin"; return GPIOCmd2; })(GPIOCmd || {}); var GPIOCmdPack; ((GPIOCmdPack2) => { GPIOCmdPack2.Configure = "r: u8 u8"; GPIOCmdPack2.PinInfo = "u8"; GPIOCmdPack2.PinInfoReport = "u8 u8 u16 u8 s"; GPIOCmdPack2.PinByLabel = "s"; GPIOCmdPack2.PinByLabelReport = "u8 u8 u16 u8 s"; GPIOCmdPack2.PinByHwPin = "u8"; GPIOCmdPack2.PinByHwPinReport = "u8 u8 u16 u8 s"; })(GPIOCmdPack || (GPIOCmdPack = {})); var SRV_GYROSCOPE = 505087730; var GyroscopeReg = /* @__PURE__ */ ((GyroscopeReg2) => { GyroscopeReg2[GyroscopeReg2["RotationRates"] = 257] = "RotationRates"; GyroscopeReg2[GyroscopeReg2["RotationRatesError"] = 262] = "RotationRatesError"; GyroscopeReg2[GyroscopeReg2["MaxRate"] = 8] = "MaxRate"; GyroscopeReg2[GyroscopeReg2["MaxRatesSupported"] = 266] = "MaxRatesSupported"; return GyroscopeReg2; })(GyroscopeReg || {}); var GyroscopeRegPack; ((GyroscopeRegPack2) => { GyroscopeRegPack2.RotationRates = "i12.20 i12.20 i12.20"; GyroscopeRegPack2.RotationRatesError = "u12.20"; GyroscopeRegPack2.MaxRate = "u12.20"; GyroscopeRegPack2.MaxRatesSupported = "r: u12.20"; })(GyroscopeRegPack || (GyroscopeRegPack = {})); var SRV_HEART_RATE = 376204740; var HeartRateVariant = /* @__PURE__ */ ((HeartRateVariant2) => { HeartRateVariant2[HeartRateVariant2["Finger"] = 1] = "Finger"; HeartRateVariant2[HeartRateVariant2["Chest"] = 2] = "Chest"; HeartRateVariant2[HeartRateVariant2["Wrist"] = 3] = "Wrist"; HeartRateVariant2[HeartRateVariant2["Pump"] = 4] = "Pump"; HeartRateVariant2[HeartRateVariant2["WebCam"] = 5] = "WebCam"; return HeartRateVariant2; })(HeartRateVariant || {}); var HeartRateReg = /* @__PURE__ */ ((HeartRateReg2) => { HeartRateReg2[HeartRateReg2["HeartRate"] = 257] = "HeartRate"; HeartRateReg2[HeartRateReg2["HeartRateError"] = 262] = "HeartRateError"; HeartRateReg2[HeartRateReg2["Variant"] = 263] = "Variant"; return HeartRateReg2; })(HeartRateReg || {}); var HeartRateRegPack; ((HeartRateRegPack2) => { HeartRateRegPack2.HeartRate = "u16.16"; HeartRateRegPack2.HeartRateError = "u16.16"; HeartRateRegPack2.Variant = "u8"; })(HeartRateRegPack || (HeartRateRegPack = {})); var SRV_HID_JOYSTICK = 437330261; var HidJoystickReg = /* @__PURE__ */ ((HidJoystickReg2) => { HidJoystickReg2[HidJoystickReg2["ButtonCount"] = 384] = "ButtonCount"; HidJoystickReg2[HidJoystickReg2["ButtonsAnalog"] = 385] = "ButtonsAnalog"; HidJoystickReg2[HidJoystickReg2["AxisCount"] = 386] = "AxisCount"; return HidJoystickReg2; })(HidJoystickReg || {}); var HidJoystickRegPack; ((HidJoystickRegPack2) => { HidJoystickRegPack2.ButtonCount = "u8"; HidJoystickRegPack2.ButtonsAnalog = "u32"; HidJoystickRegPack2.AxisCount = "u8"; })(HidJoystickRegPack || (HidJoystickRegPack = {})); var HidJoystickCmd = /* @__PURE__ */ ((HidJoystickCmd2) => { HidJoystickCmd2[HidJoystickCmd2["SetButtons"] = 128] = "SetButtons"; HidJoystickCmd2[HidJoystickCmd2["SetAxis"] = 129] = "SetAxis"; return HidJoystickCmd2; })(HidJoystickCmd || {}); var HidJoystickCmdPack; ((HidJoystickCmdPack2) => { HidJoystickCmdPack2.SetButtons = "r: u0.8"; HidJoystickCmdPack2.SetAxis = "r: i1.15"; })(HidJoystickCmdPack || (HidJoystickCmdPack = {})); var SRV_HID_KEYBOARD = 414210922; var HidKeyboardSelector = /* @__PURE__ */ ((HidKeyboardSelector2) => { HidKeyboardSelector2[HidKeyboardSelector2["None"] = 0] = "None"; HidKeyboardSelector2[HidKeyboardSelector2["ErrorRollOver"] = 1] = "ErrorRollOver"; HidKeyboardSelector2[HidKeyboardSelector2["PostFail"] = 2] = "PostFail"; HidKeyboardSelector2[HidKeyboardSelector2["ErrorUndefined"] = 3] = "ErrorUndefined"; HidKeyboardSelector2[HidKeyboardSelector2["A"] = 4] = "A"; HidKeyboardSelector2[HidKeyboardSelector2["B"] = 5] = "B"; HidKeyboardSelector2[HidKeyboardSelector2["C"] = 6] = "C"; HidKeyboardSelector2[HidKeyboardSelector2["D"] = 7] = "D"; HidKeyboardSelector2[HidKeyboardSelector2["E"] = 8] = "E"; HidKeyboardSelector2[HidKeyboardSelector2["F"] = 9] = "F"; HidKeyboardSelector2[HidKeyboardSelector2["G"] = 10] = "G"; HidKeyboardSelector2[HidKeyboardSelector2["H"] = 11] = "H"; HidKeyboardSelector2[HidKeyboardSelector2["I"] = 12] = "I"; HidKeyboardSelector2[HidKeyboardSelector2["J"] = 13] = "J"; HidKeyboardSelector2[HidKeyboardSelector2["K"] = 14] = "K"; HidKeyboardSelector2[HidKeyboardSelector2["L"] = 15] = "L"; HidKeyboardSelector2[HidKeyboardSelector2["M"] = 16] = "M"; HidKeyboardSelector2[HidKeyboardSelector2["N"] = 17] = "N"; HidKeyboardSelector2[HidKeyboardSelector2["O"] = 18] = "O"; HidKeyboardSelector2[HidKeyboardSelector2["P"] = 19] = "P"; HidKeyboardSelector2[HidKeyboardSelector2["Q"] = 20] = "Q"; HidKeyboardSelector2[HidKeyboardSelector2["R"] = 21] = "R"; HidKeyboardSelector2[HidKeyboardSelector2["S"] = 22] = "S"; HidKeyboardSelector2[HidKeyboardSelector2["T"] = 23] = "T"; HidKeyboardSelector2[HidKeyboardSelector2["U"] = 24] = "U"; HidKeyboardSelector2[HidKeyboardSelector2["V"] = 25] = "V"; HidKeyboardSelector2[HidKeyboardSelector2["W"] = 26] = "W"; HidKeyboardSelector2[HidKeyboardSelector2["X"] = 27] = "X"; HidKeyboardSelector2[HidKeyboardSelector2["Y"] = 28] = "Y"; HidKeyboardSelector2[HidKeyboardSelector2["Z"] = 29] = "Z"; HidKeyboardSelector2[HidKeyboardSelector2["_1"] = 30] = "_1"; HidKeyboardSelector2[HidKeyboardSelector2["_2"] = 31] = "_2"; HidKeyboardSelector2[HidKeyboardSelector2["_3"] = 32] = "_3"; HidKeyboardSelector2[HidKeyboardSelector2["_4"] = 33] = "_4"; HidKeyboardSelector2[HidKeyboardSelector2["_5"] = 34] = "_5"; HidKeyboardSelector2[HidKeyboardSelector2["_6"] = 35] = "_6"; HidKeyboardSelector2[HidKeyboardSelector2["_7"] = 36] = "_7"; HidKeyboardSelector2[HidKeyboardSelector2["_8"] = 37] = "_8"; HidKeyboardSelector2[HidKeyboardSelector2["_9"] = 38] = "_9"; HidKeyboardSelector2[HidKeyboardSelector2["_0"] = 39] = "_0"; HidKeyboardSelector2[HidKeyboardSelector2["Return"] = 40] = "Return"; HidKeyboardSelector2[HidKeyboardSelector2["Escape"] = 41] = "Escape"; HidKeyboardSelector2[HidKeyboardSelector2["Backspace"] = 42] = "Backspace"; HidKeyboardSelector2[HidKeyboardSelector2["Tab"] = 43] = "Tab"; HidKeyboardSelector2[HidKeyboardSelector2["Spacebar"] = 44] = "Spacebar"; HidKeyboardSelector2[HidKeyboardSelector2["Minus"] = 45] = "Minus"; HidKeyboardSelector2[HidKeyboardSelector2["Equals"] = 46] = "Equals"; HidKeyboardSelector2[HidKeyboardSelector2["LeftSquareBracket"] = 47] = "LeftSquareBracket"; HidKeyboardSelector2[HidKeyboardSelector2["RightSquareBracket"] = 48] = "RightSquareBracket"; HidKeyboardSelector2[HidKeyboardSelector2["Backslash"] = 49] = "Backslash"; HidKeyboardSelector2[HidKeyboardSelector2["NonUsHash"] = 50] = "NonUsHash"; HidKeyboardSelector2[HidKeyboardSelector2["Semicolon"] = 51] = "Semicolon"; HidKeyboardSelector2[HidKeyboardSelector2["Quote"] = 52] = "Quote"; HidKeyboardSelector2[HidKeyboardSelector2["GraveAccent"] = 53] = "GraveAccent"; HidKeyboardSelector2[HidKeyboardSelector2["Comma"] = 54] = "Comma"; HidKeyboardSelector2[HidKeyboardSelector2["Period"] = 55] = "Period"; HidKeyboardSelector2[HidKeyboardSelector2["Slash"] = 56] = "Slash"; HidKeyboardSelector2[HidKeyboardSelector2["CapsLock"] = 57] = "CapsLock"; HidKeyboardSelector2[HidKeyboardSelector2["F1"] = 58] = "F1"; HidKeyboardSelector2[HidKeyboardSelector2["F2"] = 59] = "F2"; HidKeyboardSelector2[HidKeyboardSelector2["F3"] = 60] = "F3"; HidKeyboardSelector2[HidKeyboardSelector2["F4"] = 61] = "F4"; HidKeyboardSelector2[HidKeyboardSelector2["F5"] = 62] = "F5"; HidKeyboardSelector2[HidKeyboardSelector2["F6"] = 63] = "F6"; HidKeyboardSelector2[HidKeyboardSelector2["F7"] = 64] = "F7"; HidKeyboardSelector2[HidKeyboardSelector2["F8"] = 65] = "F8"; HidKeyboardSelector2[HidKeyboardSelector2["F9"] = 66] = "F9"; HidKeyboardSelector2[HidKeyboardSelector2["F10"] = 67] = "F10"; HidKeyboardSelector2[HidKeyboardSelector2["F11"] = 68] = "F11"; HidKeyboardSelector2[HidKeyboardSelector2["F12"] = 69] = "F12"; HidKeyboardSelector2[HidKeyboardSelector2["PrintScreen"] = 70] = "PrintScreen"; HidKeyboardSelector2[HidKeyboardSelector2["ScrollLock"] = 71] = "ScrollLock"; HidKeyboardSelector2[HidKeyboardSelector2["Pause"] = 72] = "Pause"; HidKeyboardSelector2[HidKeyboardSelector2["Insert"] = 73] = "Insert"; HidKeyboardSelector2[HidKeyboardSelector2["Home"] = 74] = "Home"; HidKeyboardSelector2[HidKeyboardSelector2["PageUp"] = 75] = "PageUp"; HidKeyboardSelector2[HidKeyboardSelector2["Delete"] = 76] = "Delete"; HidKeyboardSelector2[HidKeyboardSelector2["End"] = 77] = "End"; HidKeyboardSelector2[HidKeyboardSelector2["PageDown"] = 78] = "PageDown"; HidKeyboardSelector2[HidKeyboardSelector2["RightArrow"] = 79] = "RightArrow"; HidKeyboardSelector2[HidKeyboardSelector2["LeftArrow"] = 80] = "LeftArrow"; HidKeyboardSelector2[HidKeyboardSelector2["DownArrow"] = 81] = "DownArrow"; HidKeyboardSelector2[HidKeyboardSelector2["UpArrow"] = 82] = "UpArrow"; HidKeyboardSelector2[HidKeyboardSelector2["KeypadNumLock"] = 83] = "KeypadNumLock"; HidKeyboardSelector2[HidKeyboardSelector2["KeypadDivide"] = 84] = "KeypadDivide"; HidKeyboardSelector2[HidKeyboardSelector2["KeypadMultiply"] = 85] = "KeypadMultiply"; HidKeyboardSelector2[HidKeyboardSelector2["KeypadAdd"] = 86] = "KeypadAdd"; HidKeyboardSelector2[HidKeyboardSelector2["KeypadSubtrace"] = 87] = "KeypadSubtrace"; HidKeyboardSelector2[HidKeyboardSelector2["KeypadReturn"] = 88] = "KeypadReturn"; HidKeyboardSelector2[HidKeyboardSelector2["Keypad1"] = 89] = "Keypad1"; HidKeyboardSelector2[HidKeyboardSelector2["Keypad2"] = 90] = "Keypad2"; HidKeyboardSelector2[HidKeyboardSelector2["Keypad3"] = 91] = "Keypad3"; HidKeyboardSelector2[HidKeyboardSelector2["Keypad4"] = 92] = "Keypad4"; HidKeyboardSelector2[HidKeyboardSelector2["Keypad5"] = 93] = "Keypad5"; HidKeyboardSelector2[HidKeyboardSelector2["Keypad6"] = 94] = "Keypad6"; HidKeyboardSelector2[HidKeyboardSelector2["Keypad7"] = 95] = "Keypad7"; HidKeyboardSelector2[HidKeyboardSelector2["Keypad8"] = 96] = "Keypad8"; HidKeyboardSelector2[HidKeyboardSelector2["Keypad9"] = 97] = "Keypad9"; HidKeyboardSelector2[HidKeyboardSelector2["Keypad0"] = 98] = "Keypad0"; HidKeyboardSelector2[HidKeyboardSelector2["KeypadDecimalPoint"] = 99] = "KeypadDecimalPoint"; HidKeyboardSelector2[HidKeyboardSelector2["NonUsBackslash"] = 100] = "NonUsBackslash"; HidKeyboardSelector2[HidKeyboardSelector2["Application"] = 101] = "Application"; HidKeyboardSelector2[HidKeyboardSelector2["Power"] = 102] = "Power"; HidKeyboardSelector2[HidKeyboardSelector2["KeypadEquals"] = 103] = "KeypadEquals"; HidKeyboardSelector2[HidKeyboardSelector2["F13"] = 104] = "F13"; HidKeyboardSelector2[HidKeyboardSelector2["F14"] = 105] = "F14"; HidKeyboardSelector2[HidKeyboardSelector2["F15"] = 106] = "F15"; HidKeyboardSelector2[HidKeyboardSelector2["F16"] = 107] = "F16"; HidKeyboardSelector2[HidKeyboardSelector2["F17"] = 108] = "F17"; HidKeyboardSelector2[HidKeyboardSelector2["F18"] = 109] = "F18"; HidKeyboardSelector2[HidKeyboardSelector2["F19"] = 110] = "F19"; HidKeyboardSelector2[HidKeyboardSelector2["F20"] = 111] = "F20"; HidKeyboardSelector2[HidKeyboardSelector2["F21"] = 112] = "F21"; HidKeyboardSelector2[HidKeyboardSelector2["F22"] = 113] = "F22"; HidKeyboardSelector2[HidKeyboardSelector2["F23"] = 114] = "F23"; HidKeyboardSelector2[HidKeyboardSelector2["F24"] = 115] = "F24"; HidKeyboardSelector2[HidKeyboardSelector2["Execute"] = 116] = "Execute"; HidKeyboardSelector2[HidKeyboardSelector2["Help"] = 117] = "Help"; HidKeyboardSelector2[HidKeyboardSelector2["Menu"] = 118] = "Menu"; HidKeyboardSelector2[HidKeyboardSelector2["Select"] = 119] = "Select"; HidKeyboardSelector2[HidKeyboardSelector2["Stop"] = 120] = "Stop"; HidKeyboardSelector2[HidKeyboardSelector2["Again"] = 121] = "Again"; HidKeyboardSelector2[HidKeyboardSelector2["Undo"] = 122] = "Undo"; HidKeyboardSelector2[HidKeyboardSelector2["Cut"] = 123] = "Cut"; HidKeyboardSelector2[HidKeyboardSelector2["Copy"] = 124] = "Copy"; HidKeyboardSelector2[HidKeyboardSelector2["Paste"] = 125] = "Paste"; HidKeyboardSelector2[HidKeyboardSelector2["Find"] = 126] = "Find"; HidKeyboardSelector2[HidKeyboardSelector2["Mute"] = 127] = "Mute"; HidKeyboardSelector2[HidKeyboardSelector2["VolumeUp"] = 128] = "VolumeUp"; HidKeyboardSelector2[HidKeyboardSelector2["VolumeDown"] = 129] = "VolumeDown"; return HidKeyboardSelector2; })(HidKeyboardSelector || {}); var HidKeyboardModifiers = /* @__PURE__ */ ((HidKeyboardModifiers2) => { HidKeyboardModifiers2[HidKeyboardModifiers2["None"] = 0] = "None"; HidKeyboardModifiers2[HidKeyboardModifiers2["LeftControl"] = 1] = "LeftControl"; HidKeyboardModifiers2[HidKeyboardModifiers2["LeftShift"] = 2] = "LeftShift"; HidKeyboardModifiers2[HidKeyboardModifiers2["LeftAlt"] = 4] = "LeftAlt"; HidKeyboardModifiers2[HidKeyboardModifiers2["LeftGUI"] = 8] = "LeftGUI"; HidKeyboardModifiers2[HidKeyboardModifiers2["RightControl"] = 16] = "RightControl"; HidKeyboardModifiers2[HidKeyboardModifiers2["RightShift"] = 32] = "RightShift"; HidKeyboardModifiers2[HidKeyboardModifiers2["RightAlt"] = 64] = "RightAlt"; HidKeyboardModifiers2[HidKeyboardModifiers2["RightGUI"] = 128] = "RightGUI"; return HidKeyboardModifiers2; })(HidKeyboardModifiers || {}); var HidKeyboardAction = /* @__PURE__ */ ((HidKeyboardAction3) => { HidKeyboardAction3[HidKeyboardAction3["Press"] = 0] = "Press"; HidKeyboardAction3[HidKeyboardAction3["Up"] = 1] = "Up"; HidKeyboardAction3[HidKeyboardAction3["Down"] = 2] = "Down"; return HidKeyboardAction3; })(HidKeyboardAction || {}); var HidKeyboardCmd = /* @__PURE__ */ ((HidKeyboardCmd2) => { HidKeyboardCmd2[HidKeyboardCmd2["Key"] = 128] = "Key"; HidKeyboardCmd2[HidKeyboardCmd2["Clear"] = 129] = "Clear"; return HidKeyboardCmd2; })(HidKeyboardCmd || {}); var HidKeyboardCmdPack; ((HidKeyboardCmdPack2) => { HidKeyboardCmdPack2.Key = "r: u16 u8 u8"; })(HidKeyboardCmdPack || (HidKeyboardCmdPack = {})); var SRV_HID_MOUSE = 411425820; var HidMouseButton = /* @__PURE__ */ ((HidMouseButton2) => { HidMouseButton2[HidMouseButton2["Left"] = 1] = "Left"; HidMouseButton2[HidMouseButton2["Right"] = 2] = "Right"; HidMouseButton2[HidMouseButton2["Middle"] = 4] = "Middle"; return HidMouseButton2; })(HidMouseButton || {}); var HidMouseButtonEvent = /* @__PURE__ */ ((HidMouseButtonEvent2) => { HidMouseButtonEvent2[HidMouseButtonEvent2["Up"] = 1] = "Up"; HidMouseButtonEvent2[HidMouseButtonEvent2["Down"] = 2] = "Down"; HidMouseButtonEvent2[HidMouseButtonEvent2["Click"] = 3] = "Click"; HidMouseButtonEvent2[HidMouseButtonEvent2["DoubleClick"] = 4] = "DoubleClick"; return HidMouseButtonEvent2; })(HidMouseButtonEvent || {}); var HidMouseCmd = /* @__PURE__ */ ((HidMouseCmd2) => { HidMouseCmd2[HidMouseCmd2["SetButton"] = 128] = "SetButton"; HidMouseCmd2[HidMouseCmd2["Move"] = 129] = "Move"; HidMouseCmd2[HidMouseCmd2["Wheel"] = 130] = "Wheel"; return HidMouseCmd2; })(HidMouseCmd || {}); var HidMouseCmdPack; ((HidMouseCmdPack2) => { HidMouseCmdPack2.SetButton = "u16 u8"; HidMouseCmdPack2.Move = "i16 i16 u16"; HidMouseCmdPack2.Wheel = "i16 u16"; })(HidMouseCmdPack || (HidMouseCmdPack = {})); var SRV_HUMIDITY = 382210232; var HumidityReg = /* @__PURE__ */ ((HumidityReg2) => { HumidityReg2[HumidityReg2["Humidity"] = 257] = "Humidity"; HumidityReg2[HumidityReg2["HumidityError"] = 262] = "HumidityError"; HumidityReg2[HumidityReg2["MinHumidity"] = 260] = "MinHumidity"; HumidityReg2[HumidityReg2["MaxHumidity"] = 261] = "MaxHumidity"; return HumidityReg2; })(HumidityReg || {}); var HumidityRegPack; ((HumidityRegPack2) => { HumidityRegPack2.Humidity = "u22.10"; HumidityRegPack2.HumidityError = "u22.10"; HumidityRegPack2.MinHumidity = "u22.10"; HumidityRegPack2.MaxHumidity = "u22.10"; })(HumidityRegPack || (HumidityRegPack = {})); var SRV_I2C = 471386691; var I2CStatus = /* @__PURE__ */ ((I2CStatus2) => { I2CStatus2[I2CStatus2["OK"] = 0] = "OK"; I2CStatus2[I2CStatus2["NAckAddr"] = 1] = "NAckAddr"; I2CStatus2[I2CStatus2["NAckData"] = 2] = "NAckData"; I2CStatus2[I2CStatus2["NoI2C"] = 3] = "NoI2C"; return I2CStatus2; })(I2CStatus || {}); var I2CReg = /* @__PURE__ */ ((I2CReg2) => { I2CReg2[I2CReg2["Ok"] = 384] = "Ok"; return I2CReg2; })(I2CReg || {}); var I2CRegPack; ((I2CRegPack2) => { I2CRegPack2.Ok = "u8"; })(I2CRegPack || (I2CRegPack = {})); var I2CCmd = /* @__PURE__ */ ((I2CCmd2) => { I2CCmd2[I2CCmd2["Transaction"] = 128] = "Transaction"; return I2CCmd2; })(I2CCmd || {}); var I2CCmdPack; ((I2CCmdPack2) => { I2CCmdPack2.Transaction = "u8 u8 b"; I2CCmdPack2.TransactionReport = "u8 b"; })(I2CCmdPack || (I2CCmdPack = {})); var SRV_ILLUMINANCE = 510577394; var IlluminanceReg = /* @__PURE__ */ ((IlluminanceReg2) => { IlluminanceReg2[IlluminanceReg2["Illuminance"] = 257] = "Illuminance"; IlluminanceReg2[IlluminanceReg2["IlluminanceError"] = 262] = "IlluminanceError"; return IlluminanceReg2; })(IlluminanceReg || {}); var IlluminanceRegPack; ((IlluminanceRegPack2) => { IlluminanceRegPack2.Illuminance = "u22.10"; IlluminanceRegPack2.IlluminanceError = "u22.10"; })(IlluminanceRegPack || (IlluminanceRegPack = {})); var SRV_INDEXED_SCREEN = 385496805; var IndexedScreenCmd = /* @__PURE__ */ ((IndexedScreenCmd2) => { IndexedScreenCmd2[IndexedScreenCmd2["StartUpdate"] = 129] = "StartUpdate"; IndexedScreenCmd2[IndexedScreenCmd2["SetPixels"] = 131] = "SetPixels"; return IndexedScreenCmd2; })(IndexedScreenCmd || {}); var IndexedScreenCmdPack; ((IndexedScreenCmdPack2) => { IndexedScreenCmdPack2.StartUpdate = "u16 u16 u16 u16"; IndexedScreenCmdPack2.SetPixels = "b"; })(IndexedScreenCmdPack || (IndexedScreenCmdPack = {})); var IndexedScreenReg = /* @__PURE__ */ ((IndexedScreenReg2) => { IndexedScreenReg2[IndexedScreenReg2["Brightness"] = 1] = "Brightness"; IndexedScreenReg2[IndexedScreenReg2["Palette"] = 128] = "Palette"; IndexedScreenReg2[IndexedScreenReg2["BitsPerPixel"] = 384] = "BitsPerPixel"; IndexedScreenReg2[IndexedScreenReg2["Width"] = 385] = "Width"; IndexedScreenReg2[IndexedScreenReg2["Height"] = 386] = "Height"; IndexedScreenReg2[IndexedScreenReg2["WidthMajor"] = 129] = "WidthMajor"; IndexedScreenReg2[IndexedScreenReg2["UpSampling"] = 130] = "UpSampling"; IndexedScreenReg2[IndexedScreenReg2["Rotation"] = 131] = "Rotation"; return IndexedScreenReg2; })(IndexedScreenReg || {}); var IndexedScreenRegPack; ((IndexedScreenRegPack2) => { IndexedScreenRegPack2.Brightness = "u0.8"; IndexedScreenRegPack2.Palette = "b"; IndexedScreenRegPack2.BitsPerPixel = "u8"; IndexedScreenRegPack2.Width = "u16"; IndexedScreenRegPack2.Height = "u16"; IndexedScreenRegPack2.WidthMajor = "u8"; IndexedScreenRegPack2.UpSampling = "u8"; IndexedScreenRegPack2.Rotation = "u16"; })(IndexedScreenRegPack || (IndexedScreenRegPack = {})); var SRV_INFRASTRUCTURE = 504728043; var SRV_KEYBOARD_CLIENT = 289210942; var KeyboardClientEvent = /* @__PURE__ */ ((KeyboardClientEvent2) => { KeyboardClientEvent2[KeyboardClientEvent2["Down"] = 1] = "Down"; KeyboardClientEvent2[KeyboardClientEvent2["Hold"] = 129] = "Hold"; return KeyboardClientEvent2; })(KeyboardClientEvent || {}); var KeyboardClientEventPack; ((KeyboardClientEventPack2) => { KeyboardClientEventPack2.Down = "u16"; KeyboardClientEventPack2.Hold = "u16"; })(KeyboardClientEventPack || (KeyboardClientEventPack = {})); var SRV_LED = 369743088; var CONST_LED_MAX_PIXELS_LENGTH = 64; var LedVariant = /* @__PURE__ */ ((LedVariant2) => { LedVariant2[LedVariant2["Strip"] = 1] = "Strip"; LedVariant2[LedVariant2["Ring"] = 2] = "Ring"; LedVariant2[LedVariant2["Stick"] = 3] = "Stick"; LedVariant2[LedVariant2["Jewel"] = 4] = "Jewel"; LedVariant2[LedVariant2["Matrix"] = 5] = "Matrix"; return LedVariant2; })(LedVariant || {}); var LedReg = /* @__PURE__ */ ((LedReg2) => { LedReg2[LedReg2["Pixels"] = 2] = "Pixels"; LedReg2[LedReg2["Brightness"] = 1] = "Brightness"; LedReg2[LedReg2["ActualBrightness"] = 384] = "ActualBrightness"; LedReg2[LedReg2["NumPixels"] = 386] = "NumPixels"; LedReg2[LedReg2["NumColumns"] = 387] = "NumColumns"; LedReg2[LedReg2["MaxPower"] = 7] = "MaxPower"; LedReg2[LedReg2["LedsPerPixel"] = 388] = "LedsPerPixel"; LedReg2[LedReg2["WaveLength"] = 389] = "WaveLength"; LedReg2[LedReg2["LuminousIntensity"] = 390] = "LuminousIntensity"; LedReg2[LedReg2["Variant"] = 263] = "Variant"; return LedReg2; })(LedReg || {}); var LedRegPack; ((LedRegPack2) => { LedRegPack2.Pixels = "b"; LedRegPack2.Brightness = "u0.8"; LedRegPack2.ActualBrightness = "u0.8"; LedRegPack2.NumPixels = "u16"; LedRegPack2.NumColumns = "u16"; LedRegPack2.MaxPower = "u16"; LedRegPack2.LedsPerPixel = "u16"; LedRegPack2.WaveLength = "u16"; LedRegPack2.LuminousIntensity = "u16"; LedRegPack2.Variant = "u8"; })(LedRegPack || (LedRegPack = {})); var SRV_LED_SINGLE = 506480888; var LedSingleVariant = /* @__PURE__ */ ((LedSingleVariant2) => { LedSingleVariant2[LedSingleVariant2["ThroughHole"] = 1] = "ThroughHole"; LedSingleVariant2[LedSingleVariant2["SMD"] = 2] = "SMD"; LedSingleVariant2[LedSingleVariant2["Power"] = 3] = "Power"; LedSingleVariant2[LedSingleVariant2["Bead"] = 4] = "Bead"; return LedSingleVariant2; })(LedSingleVariant || {}); var LedSingleCmd = /* @__PURE__ */ ((LedSingleCmd2) => { LedSingleCmd2[LedSingleCmd2["Animate"] = 128] = "Animate"; return LedSingleCmd2; })(LedSingleCmd || {}); var LedSingleCmdPack; ((LedSingleCmdPack2) => { LedSingleCmdPack2.Animate = "u8 u8 u8 u8"; })(LedSingleCmdPack || (LedSingleCmdPack = {})); var LedSingleReg = /* @__PURE__ */ ((LedSingleReg2) => { LedSingleReg2[LedSingleReg2["Color"] = 384] = "Color"; LedSingleReg2[LedSingleReg2["MaxPower"] = 7] = "MaxPower"; LedSingleReg2[LedSingleReg2["LedCount"] = 387] = "LedCount"; LedSingleReg2[LedSingleReg2["WaveLength"] = 385] = "WaveLength"; LedSingleReg2[LedSingleReg2["LuminousIntensity"] = 386] = "LuminousIntensity"; LedSingleReg2[LedSingleReg2["Variant"] = 263] = "Variant"; return LedSingleReg2; })(LedSingleReg || {}); var LedSingleRegPack; ((LedSingleRegPack2) => { LedSingleRegPack2.Color = "u8 u8 u8"; LedSingleRegPack2.MaxPower = "u16"; LedSingleRegPack2.LedCount = "u16"; LedSingleRegPack2.WaveLength = "u16"; LedSingleRegPack2.LuminousIntensity = "u16"; LedSingleRegPack2.Variant = "u8"; })(LedSingleRegPack || (LedSingleRegPack = {})); var SRV_LED_STRIP = 309264608; var LedStripLightType = /* @__PURE__ */ ((LedStripLightType2) => { LedStripLightType2[LedStripLightType2["WS2812B_GRB"] = 0] = "WS2812B_GRB"; LedStripLightType2[LedStripLightType2["APA102"] = 16] = "APA102"; LedStripLightType2[LedStripLightType2["SK9822"] = 17] = "SK9822"; return LedStripLightType2; })(LedStripLightType || {}); var LedStripVariant = /* @__PURE__ */ ((LedStripVariant3) => { LedStripVariant3[LedStripVariant3["Strip"] = 1] = "Strip"; LedStripVariant3[LedStripVariant3["Ring"] = 2] = "Ring"; LedStripVariant3[LedStripVariant3["Stick"] = 3] = "Stick"; LedStripVariant3[LedStripVariant3["Jewel"] = 4] = "Jewel"; LedStripVariant3[LedStripVariant3["Matrix"] = 5] = "Matrix"; return LedStripVariant3; })(LedStripVariant || {}); var LedStripReg = /* @__PURE__ */ ((LedStripReg2) => { LedStripReg2[LedStripReg2["Brightness"] = 1] = "Brightness"; LedStripReg2[LedStripReg2["ActualBrightness"] = 384] = "ActualBrightness"; LedStripReg2[LedStripReg2["LightType"] = 128] = "LightType"; LedStripReg2[LedStripReg2["NumPixels"] = 129] = "NumPixels"; LedStripReg2[LedStripReg2["NumColumns"] = 131] = "NumColumns"; LedStripReg2[LedStripReg2["MaxPower"] = 7] = "MaxPower"; LedStripReg2[LedStripReg2["MaxPixels"] = 385] = "MaxPixels"; LedStripReg2[LedStripReg2["NumRepeats"] = 130] = "NumRepeats"; LedStripReg2[LedStripReg2["Variant"] = 263] = "Variant"; return LedStripReg2; })(LedStripReg || {}); var LedStripRegPack; ((LedStripRegPack2) => { LedStripRegPack2.Brightness = "u0.8"; LedStripRegPack2.ActualBrightness = "u0.8"; LedStripRegPack2.LightType = "u8"; LedStripRegPack2.NumPixels = "u16"; LedStripRegPack2.NumColumns = "u16"; LedStripRegPack2.MaxPower = "u16"; LedStripRegPack2.MaxPixels = "u16"; LedStripRegPack2.NumRepeats = "u16"; LedStripRegPack2.Variant = "u8"; })(LedStripRegPack || (LedStripRegPack = {})); var LedStripCmd = /* @__PURE__ */ ((LedStripCmd2) => { LedStripCmd2[LedStripCmd2["Run"] = 129] = "Run"; return LedStripCmd2; })(LedStripCmd || {}); var LedStripCmdPack; ((LedStripCmdPack2) => { LedStripCmdPack2.Run = "b"; })(LedStripCmdPack || (LedStripCmdPack = {})); var SRV_LIGHT_BULB = 480970060; var LightBulbReg = /* @__PURE__ */ ((LightBulbReg2) => { LightBulbReg2[LightBulbReg2["Brightness"] = 1] = "Brightness"; LightBulbReg2[LightBulbReg2["Dimmable"] = 384] = "Dimmable"; return LightBulbReg2; })(LightBulbReg || {}); var LightBulbRegPack; ((LightBulbRegPack2) => { LightBulbRegPack2.Brightness = "u0.16"; LightBulbRegPack2.Dimmable = "u8"; })(LightBulbRegPack || (LightBulbRegPack = {})); var SRV_LIGHT_LEVEL = 400333340; var LightLevelVariant = /* @__PURE__ */ ((LightLevelVariant2) => { LightLevelVariant2[LightLevelVariant2["PhotoResistor"] = 1] = "PhotoResistor"; LightLevelVariant2[LightLevelVariant2["ReverseBiasedLED"] = 2] = "ReverseBiasedLED"; return LightLevelVariant2; })(LightLevelVariant || {}); var LightLevelReg = /* @__PURE__ */ ((LightLevelReg2) => { LightLevelReg2[LightLevelReg2["LightLevel"] = 257] = "LightLevel"; LightLevelReg2[LightLevelReg2["LightLevelError"] = 262] = "LightLevelError"; LightLevelReg2[LightLevelReg2["Variant"] = 263] = "Variant"; return LightLevelReg2; })(LightLevelReg || {}); var LightLevelRegPack; ((LightLevelRegPack2) => { LightLevelRegPack2.LightLevel = "u0.16"; LightLevelRegPack2.LightLevelError = "u0.16"; LightLevelRegPack2.Variant = "u8"; })(LightLevelRegPack || (LightLevelRegPack = {})); var SRV_LOGGER = 316415946; var LoggerPriority = /* @__PURE__ */ ((LoggerPriority2) => { LoggerPriority2[LoggerPriority2["Debug"] = 0] = "Debug"; LoggerPriority2[LoggerPriority2["Log"] = 1] = "Log"; LoggerPriority2[LoggerPriority2["Warning"] = 2] = "Warning"; LoggerPriority2[LoggerPriority2["Error"] = 3] = "Error"; LoggerPriority2[LoggerPriority2["Silent"] = 4] = "Silent"; return LoggerPriority2; })(LoggerPriority || {}); var LoggerReg = /* @__PURE__ */ ((LoggerReg2) => { LoggerReg2[LoggerReg2["MinPriority"] = 128] = "MinPriority"; return LoggerReg2; })(LoggerReg || {}); var LoggerRegPack; ((LoggerRegPack2) => { LoggerRegPack2.MinPriority = "u8"; })(LoggerRegPack || (LoggerRegPack = {})); var LoggerCmd = /* @__PURE__ */ ((LoggerCmd3) => { LoggerCmd3[LoggerCmd3["Debug"] = 128] = "Debug"; LoggerCmd3[LoggerCmd3["Log"] = 129] = "Log"; LoggerCmd3[LoggerCmd3["Warn"] = 130] = "Warn"; LoggerCmd3[LoggerCmd3["Error"] = 131] = "Error"; return LoggerCmd3; })(LoggerCmd || {}); var LoggerCmdPack; ((LoggerCmdPack2) => { LoggerCmdPack2.Debug = "s"; LoggerCmdPack2.Log = "s"; LoggerCmdPack2.Warn = "s"; LoggerCmdPack2.Error = "s"; })(LoggerCmdPack || (LoggerCmdPack = {})); var SRV_MAGNETIC_FIELD_LEVEL = 318642191; var MagneticFieldLevelVariant = /* @__PURE__ */ ((MagneticFieldLevelVariant3) => { MagneticFieldLevelVariant3[MagneticFieldLevelVariant3["AnalogNS"] = 1] = "AnalogNS"; MagneticFieldLevelVariant3[MagneticFieldLevelVariant3["AnalogN"] = 2] = "AnalogN"; MagneticFieldLevelVariant3[MagneticFieldLevelVariant3["AnalogS"] = 3] = "AnalogS"; MagneticFieldLevelVariant3[MagneticFieldLevelVariant3["DigitalNS"] = 4] = "DigitalNS"; MagneticFieldLevelVariant3[MagneticFieldLevelVariant3["DigitalN"] = 5] = "DigitalN"; MagneticFieldLevelVariant3[MagneticFieldLevelVariant3["DigitalS"] = 6] = "DigitalS"; return MagneticFieldLevelVariant3; })(MagneticFieldLevelVariant || {}); var MagneticFieldLevelReg = /* @__PURE__ */ ((MagneticFieldLevelReg2) => { MagneticFieldLevelReg2[MagneticFieldLevelReg2["Strength"] = 257] = "Strength"; MagneticFieldLevelReg2[MagneticFieldLevelReg2["Detected"] = 385] = "Detected"; MagneticFieldLevelReg2[MagneticFieldLevelReg2["Variant"] = 263] = "Variant"; return MagneticFieldLevelReg2; })(MagneticFieldLevelReg || {}); var MagneticFieldLevelRegPack; ((MagneticFieldLevelRegPack2) => { MagneticFieldLevelRegPack2.Strength = "i1.15"; MagneticFieldLevelRegPack2.Detected = "u8"; MagneticFieldLevelRegPack2.Variant = "u8"; })(MagneticFieldLevelRegPack || (MagneticFieldLevelRegPack = {})); var MagneticFieldLevelEvent = /* @__PURE__ */ ((MagneticFieldLevelEvent2) => { MagneticFieldLevelEvent2[MagneticFieldLevelEvent2["Active"] = 1] = "Active"; MagneticFieldLevelEvent2[MagneticFieldLevelEvent2["Inactive"] = 2] = "Inactive"; return MagneticFieldLevelEvent2; })(MagneticFieldLevelEvent || {}); var SRV_MAGNETOMETER = 318935176; var MagnetometerReg = /* @__PURE__ */ ((MagnetometerReg2) => { MagnetometerReg2[MagnetometerReg2["Forces"] = 257] = "Forces"; MagnetometerReg2[MagnetometerReg2["ForcesError"] = 262] = "ForcesError"; return MagnetometerReg2; })(MagnetometerReg || {}); var MagnetometerRegPack; ((MagnetometerRegPack2) => { MagnetometerRegPack2.Forces = "i32 i32 i32"; MagnetometerRegPack2.ForcesError = "i32"; })(MagnetometerRegPack || (MagnetometerRegPack = {})); var MagnetometerCmd = /* @__PURE__ */ ((MagnetometerCmd2) => { MagnetometerCmd2[MagnetometerCmd2["Calibrate"] = 2] = "Calibrate"; return MagnetometerCmd2; })(MagnetometerCmd || {}); var SRV_MATRIX_KEYPAD = 319172040; var MatrixKeypadVariant = /* @__PURE__ */ ((MatrixKeypadVariant2) => { MatrixKeypadVariant2[MatrixKeypadVariant2["Membrane"] = 1] = "Membrane"; MatrixKeypadVariant2[MatrixKeypadVariant2["Keyboard"] = 2] = "Keyboard"; MatrixKeypadVariant2[MatrixKeypadVariant2["Elastomer"] = 3] = "Elastomer"; MatrixKeypadVariant2[MatrixKeypadVariant2["ElastomerLEDPixel"] = 4] = "ElastomerLEDPixel"; return MatrixKeypadVariant2; })(MatrixKeypadVariant || {}); var MatrixKeypadReg = /* @__PURE__ */ ((MatrixKeypadReg2) => { MatrixKeypadReg2[MatrixKeypadReg2["Pressed"] = 257] = "Pressed"; MatrixKeypadReg2[MatrixKeypadReg2["Rows"] = 384] = "Rows"; MatrixKeypadReg2[MatrixKeypadReg2["Columns"] = 385] = "Columns"; MatrixKeypadReg2[MatrixKeypadReg2["Labels"] = 386] = "Labels"; MatrixKeypadReg2[MatrixKeypadReg2["Variant"] = 263] = "Variant"; return MatrixKeypadReg2; })(MatrixKeypadReg || {}); var MatrixKeypadRegPack; ((MatrixKeypadRegPack2) => { MatrixKeypadRegPack2.Pressed = "r: u8"; MatrixKeypadRegPack2.Rows = "u8"; MatrixKeypadRegPack2.Columns = "u8"; MatrixKeypadRegPack2.Labels = "r: z"; MatrixKeypadRegPack2.Variant = "u8"; })(MatrixKeypadRegPack || (MatrixKeypadRegPack = {})); var MatrixKeypadEvent = /* @__PURE__ */ ((MatrixKeypadEvent2) => { MatrixKeypadEvent2[MatrixKeypadEvent2["Down"] = 1] = "Down"; MatrixKeypadEvent2[MatrixKeypadEvent2["Up"] = 2] = "Up"; MatrixKeypadEvent2[MatrixKeypadEvent2["Click"] = 128] = "Click"; MatrixKeypadEvent2[MatrixKeypadEvent2["LongClick"] = 129] = "LongClick"; return MatrixKeypadEvent2; })(MatrixKeypadEvent || {}); var MatrixKeypadEventPack; ((MatrixKeypadEventPack2) => { MatrixKeypadEventPack2.Down = "u8"; MatrixKeypadEventPack2.Up = "u8"; MatrixKeypadEventPack2.Click = "u8"; MatrixKeypadEventPack2.LongClick = "u8"; })(MatrixKeypadEventPack || (MatrixKeypadEventPack = {})); var SRV_MICROPHONE = 289254534; var MicrophoneCmd = /* @__PURE__ */ ((MicrophoneCmd2) => { MicrophoneCmd2[MicrophoneCmd2["Sample"] = 129] = "Sample"; return MicrophoneCmd2; })(MicrophoneCmd || {}); var MicrophoneCmdPack; ((MicrophoneCmdPack2) => { MicrophoneCmdPack2.Sample = "b[12] u32"; })(MicrophoneCmdPack || (MicrophoneCmdPack = {})); var MicrophoneReg = /* @__PURE__ */ ((MicrophoneReg2) => { MicrophoneReg2[MicrophoneReg2["SamplingPeriod"] = 128] = "SamplingPeriod"; return MicrophoneReg2; })(MicrophoneReg || {}); var MicrophoneRegPack; ((MicrophoneRegPack2) => { MicrophoneRegPack2.SamplingPeriod = "u32"; })(MicrophoneRegPack || (MicrophoneRegPack = {})); var SRV_MIDI_OUTPUT = 444894423; var MidiOutputReg = /* @__PURE__ */ ((MidiOutputReg2) => { MidiOutputReg2[MidiOutputReg2["Enabled"] = 1] = "Enabled"; return MidiOutputReg2; })(MidiOutputReg || {}); var MidiOutputRegPack; ((MidiOutputRegPack2) => { MidiOutputRegPack2.Enabled = "u8"; })(MidiOutputRegPack || (MidiOutputRegPack = {})); var MidiOutputCmd = /* @__PURE__ */ ((MidiOutputCmd2) => { MidiOutputCmd2[MidiOutputCmd2["Clear"] = 128] = "Clear"; MidiOutputCmd2[MidiOutputCmd2["Send"] = 129] = "Send"; return MidiOutputCmd2; })(MidiOutputCmd || {}); var MidiOutputCmdPack; ((MidiOutputCmdPack2) => { MidiOutputCmdPack2.Send = "b"; })(MidiOutputCmdPack || (MidiOutputCmdPack = {})); var SRV_MODEL_RUNNER = 336566904; var ModelRunnerModelFormat = /* @__PURE__ */ ((ModelRunnerModelFormat2) => { ModelRunnerModelFormat2[ModelRunnerModelFormat2["TFLite"] = 860636756] = "TFLite"; ModelRunnerModelFormat2[ModelRunnerModelFormat2["ML4F"] = 809963362] = "ML4F"; ModelRunnerModelFormat2[ModelRunnerModelFormat2["EdgeImpulseCompiled"] = 810961221] = "EdgeImpulseCompiled"; return ModelRunnerModelFormat2; })(ModelRunnerModelFormat || {}); var ModelRunnerCmd = /* @__PURE__ */ ((ModelRunnerCmd2) => { ModelRunnerCmd2[ModelRunnerCmd2["SetModel"] = 128] = "SetModel"; ModelRunnerCmd2[ModelRunnerCmd2["Predict"] = 129] = "Predict"; return ModelRunnerCmd2; })(ModelRunnerCmd || {}); var ModelRunnerCmdPack; ((ModelRunnerCmdPack2) => { ModelRunnerCmdPack2.SetModel = "u32"; ModelRunnerCmdPack2.SetModelReport = "u16"; ModelRunnerCmdPack2.Predict = "b[12]"; ModelRunnerCmdPack2.PredictReport = "u16"; })(ModelRunnerCmdPack || (ModelRunnerCmdPack = {})); var ModelRunnerReg = /* @__PURE__ */ ((ModelRunnerReg2) => { ModelRunnerReg2[ModelRunnerReg2["AutoInvokeEvery"] = 128] = "AutoInvokeEvery"; ModelRunnerReg2[ModelRunnerReg2["Outputs"] = 257] = "Outputs"; ModelRunnerReg2[ModelRunnerReg2["InputShape"] = 384] = "InputShape"; ModelRunnerReg2[ModelRunnerReg2["OutputShape"] = 385] = "OutputShape"; ModelRunnerReg2[ModelRunnerReg2["LastRunTime"] = 386] = "LastRunTime"; ModelRunnerReg2[ModelRunnerReg2["AllocatedArenaSize"] = 387] = "AllocatedArenaSize"; ModelRunnerReg2[ModelRunnerReg2["ModelSize"] = 388] = "ModelSize"; ModelRunnerReg2[ModelRunnerReg2["LastError"] = 389] = "LastError"; ModelRunnerReg2[ModelRunnerReg2["Format"] = 390] = "Format"; ModelRunnerReg2[ModelRunnerReg2["FormatVersion"] = 391] = "FormatVersion"; ModelRunnerReg2[ModelRunnerReg2["Parallel"] = 392] = "Parallel"; return ModelRunnerReg2; })(ModelRunnerReg || {}); var ModelRunnerRegPack; ((ModelRunnerRegPack2) => { ModelRunnerRegPack2.AutoInvokeEvery = "u16"; ModelRunnerRegPack2.Outputs = "r: f32"; ModelRunnerRegPack2.InputShape = "r: u16"; ModelRunnerRegPack2.OutputShape = "r: u16"; ModelRunnerRegPack2.LastRunTime = "u32"; ModelRunnerRegPack2.AllocatedArenaSize = "u32"; ModelRunnerRegPack2.ModelSize = "u32"; ModelRunnerRegPack2.LastError = "s"; ModelRunnerRegPack2.Format = "u32"; ModelRunnerRegPack2.FormatVersion = "u32"; ModelRunnerRegPack2.Parallel = "u8"; })(ModelRunnerRegPack || (ModelRunnerRegPack = {})); var SRV_MOTION = 293185353; var MotionVariant = /* @__PURE__ */ ((MotionVariant2) => { MotionVariant2[MotionVariant2["PIR"] = 1] = "PIR"; return MotionVariant2; })(MotionVariant || {}); var MotionReg = /* @__PURE__ */ ((MotionReg2) => { MotionReg2[MotionReg2["Moving"] = 257] = "Moving"; MotionReg2[MotionReg2["MaxDistance"] = 384] = "MaxDistance"; MotionReg2[MotionReg2["Angle"] = 385] = "Angle"; MotionReg2[MotionReg2["Variant"] = 263] = "Variant"; return MotionReg2; })(MotionReg || {}); var MotionRegPack; ((MotionRegPack2) => { MotionRegPack2.Moving = "u8"; MotionRegPack2.MaxDistance = "u16.16"; MotionRegPack2.Angle = "u16"; MotionRegPack2.Variant = "u8"; })(MotionRegPack || (MotionRegPack = {})); var MotionEvent = /* @__PURE__ */ ((MotionEvent2) => { MotionEvent2[MotionEvent2["Movement"] = 1] = "Movement"; return MotionEvent2; })(MotionEvent || {}); var SRV_MOTOR = 385895640; var MotorReg = /* @__PURE__ */ ((MotorReg2) => { MotorReg2[MotorReg2["Speed"] = 2] = "Speed"; MotorReg2[MotorReg2["Enabled"] = 1] = "Enabled"; MotorReg2[MotorReg2["LoadTorque"] = 384] = "LoadTorque"; MotorReg2[MotorReg2["LoadRotationSpeed"] = 385] = "LoadRotationSpeed"; MotorReg2[MotorReg2["Reversible"] = 386] = "Reversible"; return MotorReg2; })(MotorReg || {}); var MotorRegPack; ((MotorRegPack2) => { MotorRegPack2.Speed = "i1.15"; MotorRegPack2.Enabled = "u8"; MotorRegPack2.LoadTorque = "u16.16"; MotorRegPack2.LoadRotationSpeed = "u16.16"; MotorRegPack2.Reversible = "u8"; })(MotorRegPack || (MotorRegPack = {})); var SRV_MULTITOUCH = 487664309; var MultitouchReg = /* @__PURE__ */ ((MultitouchReg2) => { MultitouchReg2[MultitouchReg2["Capacity"] = 257] = "Capacity"; return MultitouchReg2; })(MultitouchReg || {}); var MultitouchRegPack; ((MultitouchRegPack2) => { MultitouchRegPack2.Capacity = "r: i16"; })(MultitouchRegPack || (MultitouchRegPack = {})); var MultitouchEvent = /* @__PURE__ */ ((MultitouchEvent2) => { MultitouchEvent2[MultitouchEvent2["Touch"] = 1] = "Touch"; MultitouchEvent2[MultitouchEvent2["Release"] = 2] = "Release"; MultitouchEvent2[MultitouchEvent2["Tap"] = 128] = "Tap"; MultitouchEvent2[MultitouchEvent2["LongPress"] = 129] = "LongPress"; MultitouchEvent2[MultitouchEvent2["SwipePos"] = 144] = "SwipePos"; MultitouchEvent2[MultitouchEvent2["SwipeNeg"] = 145] = "SwipeNeg"; return MultitouchEvent2; })(MultitouchEvent || {}); var MultitouchEventPack; ((MultitouchEventPack2) => { MultitouchEventPack2.Touch = "u8"; MultitouchEventPack2.Release = "u8"; MultitouchEventPack2.Tap = "u8"; MultitouchEventPack2.LongPress = "u8"; MultitouchEventPack2.SwipePos = "u16 u8 u8"; MultitouchEventPack2.SwipeNeg = "u16 u8 u8"; })(MultitouchEventPack || (MultitouchEventPack = {})); var SRV_PCCONTROLLER = 289212807; var PCControllerCmd = /* @__PURE__ */ ((PCControllerCmd2) => { PCControllerCmd2[PCControllerCmd2["OpenUrl"] = 128] = "OpenUrl"; PCControllerCmd2[PCControllerCmd2["StartApp"] = 129] = "StartApp"; PCControllerCmd2[PCControllerCmd2["SendText"] = 130] = "SendText"; PCControllerCmd2[PCControllerCmd2["RunScript"] = 131] = "RunScript"; return PCControllerCmd2; })(PCControllerCmd || {}); var PCControllerCmdPack; ((PCControllerCmdPack2) => { PCControllerCmdPack2.OpenUrl = "s"; PCControllerCmdPack2.StartApp = "s"; PCControllerCmdPack2.SendText = "s"; PCControllerCmdPack2.RunScript = "s"; })(PCControllerCmdPack || (PCControllerCmdPack = {})); var SRV_PCMONITOR = 409107221; var PCMonitorReg = /* @__PURE__ */ ((PCMonitorReg2) => { PCMonitorReg2[PCMonitorReg2["CpuUsage"] = 400] = "CpuUsage"; PCMonitorReg2[PCMonitorReg2["CpuTemperature"] = 401] = "CpuTemperature"; PCMonitorReg2[PCMonitorReg2["RamUsage"] = 402] = "RamUsage"; PCMonitorReg2[PCMonitorReg2["GpuInformation"] = 403] = "GpuInformation"; PCMonitorReg2[PCMonitorReg2["NetworkInformation"] = 405] = "NetworkInformation"; return PCMonitorReg2; })(PCMonitorReg || {}); var PCMonitorRegPack; ((PCMonitorRegPack2) => { PCMonitorRegPack2.CpuUsage = "u8"; PCMonitorRegPack2.CpuTemperature = "u8"; PCMonitorRegPack2.RamUsage = "u8"; PCMonitorRegPack2.GpuInformation = "u8 u8"; PCMonitorRegPack2.NetworkInformation = "u16 u16"; })(PCMonitorRegPack || (PCMonitorRegPack = {})); var SRV_PLANAR_POSITION = 499351381; var PlanarPositionVariant = /* @__PURE__ */ ((PlanarPositionVariant2) => { PlanarPositionVariant2[PlanarPositionVariant2["OpticalMousePosition"] = 1] = "OpticalMousePosition"; return PlanarPositionVariant2; })(PlanarPositionVariant || {}); var PlanarPositionReg = /* @__PURE__ */ ((PlanarPositionReg2) => { PlanarPositionReg2[PlanarPositionReg2["Position"] = 257] = "Position"; PlanarPositionReg2[PlanarPositionReg2["Variant"] = 263] = "Variant"; return PlanarPositionReg2; })(PlanarPositionReg || {}); var PlanarPositionRegPack; ((PlanarPositionRegPack2) => { PlanarPositionRegPack2.Position = "i22.10 i22.10"; PlanarPositionRegPack2.Variant = "u8"; })(PlanarPositionRegPack || (PlanarPositionRegPack = {})); var SRV_POTENTIOMETER = 522667846; var PotentiometerVariant = /* @__PURE__ */ ((PotentiometerVariant2) => { PotentiometerVariant2[PotentiometerVariant2["Slider"] = 1] = "Slider"; PotentiometerVariant2[PotentiometerVariant2["Rotary"] = 2] = "Rotary"; PotentiometerVariant2[PotentiometerVariant2["Hall"] = 3] = "Hall"; return PotentiometerVariant2; })(PotentiometerVariant || {}); var PotentiometerReg = /* @__PURE__ */ ((PotentiometerReg2) => { PotentiometerReg2[PotentiometerReg2["Position"] = 257] = "Position"; PotentiometerReg2[PotentiometerReg2["Variant"] = 263] = "Variant"; return PotentiometerReg2; })(PotentiometerReg || {}); var PotentiometerRegPack; ((PotentiometerRegPack2) => { PotentiometerRegPack2.Position = "u0.16"; PotentiometerRegPack2.Variant = "u8"; })(PotentiometerRegPack || (PotentiometerRegPack = {})); var SRV_POWER = 530893146; var PowerPowerStatus = /* @__PURE__ */ ((PowerPowerStatus2) => { PowerPowerStatus2[PowerPowerStatus2["Disallowed"] = 0] = "Disallowed"; PowerPowerStatus2[PowerPowerStatus2["Powering"] = 1] = "Powering"; PowerPowerStatus2[PowerPowerStatus2["Overload"] = 2] = "Overload"; PowerPowerStatus2[PowerPowerStatus2["Overprovision"] = 3] = "Overprovision"; return PowerPowerStatus2; })(PowerPowerStatus || {}); var PowerReg = /* @__PURE__ */ ((PowerReg2) => { PowerReg2[PowerReg2["Allowed"] = 1] = "Allowed"; PowerReg2[PowerReg2["MaxPower"] = 7] = "MaxPower"; PowerReg2[PowerReg2["PowerStatus"] = 385] = "PowerStatus"; PowerReg2[PowerReg2["CurrentDraw"] = 257] = "CurrentDraw"; PowerReg2[PowerReg2["BatteryVoltage"] = 384] = "BatteryVoltage"; PowerReg2[PowerReg2["BatteryCharge"] = 386] = "BatteryCharge"; PowerReg2[PowerReg2["BatteryCapacity"] = 387] = "BatteryCapacity"; PowerReg2[PowerReg2["KeepOnPulseDuration"] = 128] = "KeepOnPulseDuration"; PowerReg2[PowerReg2["KeepOnPulsePeriod"] = 129] = "KeepOnPulsePeriod"; return PowerReg2; })(PowerReg || {}); var PowerRegPack; ((PowerRegPack2) => { PowerRegPack2.Allowed = "u8"; PowerRegPack2.MaxPower = "u16"; PowerRegPack2.PowerStatus = "u8"; PowerRegPack2.CurrentDraw = "u16"; PowerRegPack2.BatteryVoltage = "u16"; PowerRegPack2.BatteryCharge = "u0.16"; PowerRegPack2.BatteryCapacity = "u32"; PowerRegPack2.KeepOnPulseDuration = "u16"; PowerRegPack2.KeepOnPulsePeriod = "u16"; })(PowerRegPack || (PowerRegPack = {})); var PowerCmd = /* @__PURE__ */ ((PowerCmd2) => { PowerCmd2[PowerCmd2["Shutdown"] = 128] = "Shutdown"; return PowerCmd2; })(PowerCmd || {}); var PowerEvent = /* @__PURE__ */ ((PowerEvent2) => { PowerEvent2[PowerEvent2["PowerStatusChanged"] = 3] = "PowerStatusChanged"; return PowerEvent2; })(PowerEvent || {}); var PowerEventPack; ((PowerEventPack2) => { PowerEventPack2.PowerStatusChanged = "u8"; })(PowerEventPack || (PowerEventPack = {})); var SRV_POWER_SUPPLY = 524302175; var PowerSupplyReg = /* @__PURE__ */ ((PowerSupplyReg2) => { PowerSupplyReg2[PowerSupplyReg2["Enabled"] = 1] = "Enabled"; PowerSupplyReg2[PowerSupplyReg2["OutputVoltage"] = 2] = "OutputVoltage"; PowerSupplyReg2[PowerSupplyReg2["MinimumVoltage"] = 272] = "MinimumVoltage"; PowerSupplyReg2[PowerSupplyReg2["MaximumVoltage"] = 273] = "MaximumVoltage"; return PowerSupplyReg2; })(PowerSupplyReg || {}); var PowerSupplyRegPack; ((PowerSupplyRegPack2) => { PowerSupplyRegPack2.Enabled = "u8"; PowerSupplyRegPack2.OutputVoltage = "f64"; PowerSupplyRegPack2.MinimumVoltage = "f64"; PowerSupplyRegPack2.MaximumVoltage = "f64"; })(PowerSupplyRegPack || (PowerSupplyRegPack = {})); var SRV_PRESSURE_BUTTON = 672612547; var PressureButtonReg = /* @__PURE__ */ ((PressureButtonReg2) => { PressureButtonReg2[PressureButtonReg2["Threshold"] = 6] = "Threshold"; return PressureButtonReg2; })(PressureButtonReg || {}); var PressureButtonRegPack; ((PressureButtonRegPack2) => { PressureButtonRegPack2.Threshold = "u0.16"; })(PressureButtonRegPack || (PressureButtonRegPack = {})); var SRV_PROTO_TEST = 382158442; var ProtoTestReg = /* @__PURE__ */ ((ProtoTestReg2) => { ProtoTestReg2[ProtoTestReg2["RwBool"] = 129] = "RwBool"; ProtoTestReg2[ProtoTestReg2["RoBool"] = 385] = "RoBool"; ProtoTestReg2[ProtoTestReg2["RwU32"] = 130] = "RwU32"; ProtoTestReg2[ProtoTestReg2["RoU32"] = 386] = "RoU32"; ProtoTestReg2[ProtoTestReg2["RwI32"] = 131] = "RwI32"; ProtoTestReg2[ProtoTestReg2["RoI32"] = 387] = "RoI32"; ProtoTestReg2[ProtoTestReg2["RwString"] = 132] = "RwString"; ProtoTestReg2[ProtoTestReg2["RoString"] = 388] = "RoString"; ProtoTestReg2[ProtoTestReg2["RwBytes"] = 133] = "RwBytes"; ProtoTestReg2[ProtoTestReg2["RoBytes"] = 389] = "RoBytes"; ProtoTestReg2[ProtoTestReg2["RwI8U8U16I32"] = 134] = "RwI8U8U16I32"; ProtoTestReg2[ProtoTestReg2["RoI8U8U16I32"] = 390] = "RoI8U8U16I32"; ProtoTestReg2[ProtoTestReg2["RwU8String"] = 135] = "RwU8String"; ProtoTestReg2[ProtoTestReg2["RoU8String"] = 391] = "RoU8String"; return ProtoTestReg2; })(ProtoTestReg || {}); var ProtoTestRegPack; ((ProtoTestRegPack2) => { ProtoTestRegPack2.RwBool = "u8"; ProtoTestRegPack2.RoBool = "u8"; ProtoTestRegPack2.RwU32 = "u32"; ProtoTestRegPack2.RoU32 = "u32"; ProtoTestRegPack2.RwI32 = "i32"; ProtoTestRegPack2.RoI32 = "i32"; ProtoTestRegPack2.RwString = "s"; ProtoTestRegPack2.RoString = "s"; ProtoTestRegPack2.RwBytes = "b"; ProtoTestRegPack2.RoBytes = "b"; ProtoTestRegPack2.RwI8U8U16I32 = "i8 u8 u16 i32"; ProtoTestRegPack2.RoI8U8U16I32 = "i8 u8 u16 i32"; ProtoTestRegPack2.RwU8String = "u8 s"; ProtoTestRegPack2.RoU8String = "u8 s"; })(ProtoTestRegPack || (ProtoTestRegPack = {})); var ProtoTestEvent = /* @__PURE__ */ ((ProtoTestEvent2) => { ProtoTestEvent2[ProtoTestEvent2["EBool"] = 129] = "EBool"; ProtoTestEvent2[ProtoTestEvent2["EU32"] = 130] = "EU32"; ProtoTestEvent2[ProtoTestEvent2["EI32"] = 131] = "EI32"; ProtoTestEvent2[ProtoTestEvent2["EString"] = 132] = "EString"; ProtoTestEvent2[ProtoTestEvent2["EBytes"] = 133] = "EBytes"; ProtoTestEvent2[ProtoTestEvent2["EI8U8U16I32"] = 134] = "EI8U8U16I32"; ProtoTestEvent2[ProtoTestEvent2["EU8String"] = 135] = "EU8String"; return ProtoTestEvent2; })(ProtoTestEvent || {}); var ProtoTestEventPack; ((ProtoTestEventPack2) => { ProtoTestEventPack2.EBool = "u8"; ProtoTestEventPack2.EU32 = "u32"; ProtoTestEventPack2.EI32 = "i32"; ProtoTestEventPack2.EString = "s"; ProtoTestEventPack2.EBytes = "b"; ProtoTestEventPack2.EI8U8U16I32 = "i8 u8 u16 i32"; ProtoTestEventPack2.EU8String = "u8 s"; })(ProtoTestEventPack || (ProtoTestEventPack = {})); var ProtoTestCmd = /* @__PURE__ */ ((ProtoTestCmd2) => { ProtoTestCmd2[ProtoTestCmd2["CBool"] = 129] = "CBool"; ProtoTestCmd2[ProtoTestCmd2["CU32"] = 130] = "CU32"; ProtoTestCmd2[ProtoTestCmd2["CI32"] = 131] = "CI32"; ProtoTestCmd2[ProtoTestCmd2["CString"] = 132] = "CString"; ProtoTestCmd2[ProtoTestCmd2["CBytes"] = 133] = "CBytes"; ProtoTestCmd2[ProtoTestCmd2["CI8U8U16I32"] = 134] = "CI8U8U16I32"; ProtoTestCmd2[ProtoTestCmd2["CU8String"] = 135] = "CU8String"; ProtoTestCmd2[ProtoTestCmd2["CReportPipe"] = 144] = "CReportPipe"; return ProtoTestCmd2; })(ProtoTestCmd || {}); var ProtoTestCmdPack; ((ProtoTestCmdPack2) => { ProtoTestCmdPack2.CBool = "u8"; ProtoTestCmdPack2.CU32 = "u32"; ProtoTestCmdPack2.CI32 = "i32"; ProtoTestCmdPack2.CString = "s"; ProtoTestCmdPack2.CBytes = "b"; ProtoTestCmdPack2.CI8U8U16I32 = "i8 u8 u16 i32"; ProtoTestCmdPack2.CU8String = "u8 s"; ProtoTestCmdPack2.CReportPipe = "b[12]"; })(ProtoTestCmdPack || (ProtoTestCmdPack = {})); var ProtoTestPipe = /* @__PURE__ */ ((ProtoTestPipe2) => { })(ProtoTestPipe || {}); var ProtoTestPipePack; ((ProtoTestPipePack2) => { ProtoTestPipePack2.PBytes = "u8"; })(ProtoTestPipePack || (ProtoTestPipePack = {})); var SRV_PROXY = 384932169; var SRV_PULSE_OXIMETER = 280710838; var PulseOximeterReg = /* @__PURE__ */ ((PulseOximeterReg2) => { PulseOximeterReg2[PulseOximeterReg2["Oxygen"] = 257] = "Oxygen"; PulseOximeterReg2[PulseOximeterReg2["OxygenError"] = 262] = "OxygenError"; return PulseOximeterReg2; })(PulseOximeterReg || {}); var PulseOximeterRegPack; ((PulseOximeterRegPack2) => { PulseOximeterRegPack2.Oxygen = "u8.8"; PulseOximeterRegPack2.OxygenError = "u8.8"; })(PulseOximeterRegPack || (PulseOximeterRegPack = {})); var SRV_RAIN_GAUGE = 326323349; var RainGaugeReg = /* @__PURE__ */ ((RainGaugeReg2) => { RainGaugeReg2[RainGaugeReg2["Precipitation"] = 257] = "Precipitation"; RainGaugeReg2[RainGaugeReg2["PrecipitationPrecision"] = 264] = "PrecipitationPrecision"; return RainGaugeReg2; })(RainGaugeReg || {}); var RainGaugeRegPack; ((RainGaugeRegPack2) => { RainGaugeRegPack2.Precipitation = "u16.16"; RainGaugeRegPack2.PrecipitationPrecision = "u16.16"; })(RainGaugeRegPack || (RainGaugeRegPack = {})); var SRV_REAL_TIME_CLOCK = 445323816; var RealTimeClockVariant = /* @__PURE__ */ ((RealTimeClockVariant2) => { RealTimeClockVariant2[RealTimeClockVariant2["Computer"] = 1] = "Computer"; RealTimeClockVariant2[RealTimeClockVariant2["Crystal"] = 2] = "Crystal"; RealTimeClockVariant2[RealTimeClockVariant2["Cuckoo"] = 3] = "Cuckoo"; return RealTimeClockVariant2; })(RealTimeClockVariant || {}); var RealTimeClockReg = /* @__PURE__ */ ((RealTimeClockReg2) => { RealTimeClockReg2[RealTimeClockReg2["LocalTime"] = 257] = "LocalTime"; RealTimeClockReg2[RealTimeClockReg2["Drift"] = 384] = "Drift"; RealTimeClockReg2[RealTimeClockReg2["Precision"] = 385] = "Precision"; RealTimeClockReg2[RealTimeClockReg2["Variant"] = 263] = "Variant"; return RealTimeClockReg2; })(RealTimeClockReg || {}); var RealTimeClockRegPack; ((RealTimeClockRegPack2) => { RealTimeClockRegPack2.LocalTime = "u16 u8 u8 u8 u8 u8 u8"; RealTimeClockRegPack2.Drift = "u16.16"; RealTimeClockRegPack2.Precision = "u16.16"; RealTimeClockRegPack2.Variant = "u8"; })(RealTimeClockRegPack || (RealTimeClockRegPack = {})); var RealTimeClockCmd = /* @__PURE__ */ ((RealTimeClockCmd2) => { RealTimeClockCmd2[RealTimeClockCmd2["SetTime"] = 128] = "SetTime"; return RealTimeClockCmd2; })(RealTimeClockCmd || {}); var RealTimeClockCmdPack; ((RealTimeClockCmdPack2) => { RealTimeClockCmdPack2.SetTime = "u16 u8 u8 u8 u8 u8 u8"; })(RealTimeClockCmdPack || (RealTimeClockCmdPack = {})); var SRV_REFLECTED_LIGHT = 309087410; var ReflectedLightVariant = /* @__PURE__ */ ((ReflectedLightVariant2) => { ReflectedLightVariant2[ReflectedLightVariant2["InfraredDigital"] = 1] = "InfraredDigital"; ReflectedLightVariant2[ReflectedLightVariant2["InfraredAnalog"] = 2] = "InfraredAnalog"; return ReflectedLightVariant2; })(ReflectedLightVariant || {}); var ReflectedLightReg = /* @__PURE__ */ ((ReflectedLightReg2) => { ReflectedLightReg2[ReflectedLightReg2["Brightness"] = 257] = "Brightness"; ReflectedLightReg2[ReflectedLightReg2["Variant"] = 263] = "Variant"; return ReflectedLightReg2; })(ReflectedLightReg || {}); var ReflectedLightRegPack; ((ReflectedLightRegPack2) => { ReflectedLightRegPack2.Brightness = "u0.16"; ReflectedLightRegPack2.Variant = "u8"; })(ReflectedLightRegPack || (ReflectedLightRegPack = {})); var SRV_RELAY = 406840918; var RelayVariant = /* @__PURE__ */ ((RelayVariant2) => { RelayVariant2[RelayVariant2["Electromechanical"] = 1] = "Electromechanical"; RelayVariant2[RelayVariant2["SolidState"] = 2] = "SolidState"; RelayVariant2[RelayVariant2["Reed"] = 3] = "Reed"; return RelayVariant2; })(RelayVariant || {}); var RelayReg = /* @__PURE__ */ ((RelayReg2) => { RelayReg2[RelayReg2["Active"] = 1] = "Active"; RelayReg2[RelayReg2["Variant"] = 263] = "Variant"; RelayReg2[RelayReg2["MaxSwitchingCurrent"] = 384] = "MaxSwitchingCurrent"; return RelayReg2; })(RelayReg || {}); var RelayRegPack; ((RelayRegPack2) => { RelayRegPack2.Active = "u8"; RelayRegPack2.Variant = "u8"; RelayRegPack2.MaxSwitchingCurrent = "u32"; })(RelayRegPack || (RelayRegPack = {})); var SRV_RNG = 394916002; var RngVariant = /* @__PURE__ */ ((RngVariant2) => { RngVariant2[RngVariant2["Quantum"] = 1] = "Quantum"; RngVariant2[RngVariant2["ADCNoise"] = 2] = "ADCNoise"; RngVariant2[RngVariant2["WebCrypto"] = 3] = "WebCrypto"; return RngVariant2; })(RngVariant || {}); var RngReg = /* @__PURE__ */ ((RngReg2) => { RngReg2[RngReg2["Random"] = 384] = "Random"; RngReg2[RngReg2["Variant"] = 263] = "Variant"; return RngReg2; })(RngReg || {}); var RngRegPack; ((RngRegPack2) => { RngRegPack2.Random = "b"; RngRegPack2.Variant = "u8"; })(RngRegPack || (RngRegPack = {})); var SRV_ROLE_MANAGER = 508264038; var RoleManagerReg = /* @__PURE__ */ ((RoleManagerReg2) => { RoleManagerReg2[RoleManagerReg2["AutoBind"] = 128] = "AutoBind"; RoleManagerReg2[RoleManagerReg2["AllRolesAllocated"] = 385] = "AllRolesAllocated"; return RoleManagerReg2; })(RoleManagerReg || {}); var RoleManagerRegPack; ((RoleManagerRegPack2) => { RoleManagerRegPack2.AutoBind = "u8"; RoleManagerRegPack2.AllRolesAllocated = "u8"; })(RoleManagerRegPack || (RoleManagerRegPack = {})); var RoleManagerCmd = /* @__PURE__ */ ((RoleManagerCmd2) => { RoleManagerCmd2[RoleManagerCmd2["SetRole"] = 129] = "SetRole"; RoleManagerCmd2[RoleManagerCmd2["ClearAllRoles"] = 132] = "ClearAllRoles"; RoleManagerCmd2[RoleManagerCmd2["ListRoles"] = 131] = "ListRoles"; return RoleManagerCmd2; })(RoleManagerCmd || {}); var RoleManagerCmdPack; ((RoleManagerCmdPack2) => { RoleManagerCmdPack2.SetRole = "b[8] u8 s"; RoleManagerCmdPack2.ListRoles = "b[12]"; })(RoleManagerCmdPack || (RoleManagerCmdPack = {})); var RoleManagerPipe = /* @__PURE__ */ ((RoleManagerPipe2) => { })(RoleManagerPipe || {}); var RoleManagerPipePack; ((RoleManagerPipePack2) => { RoleManagerPipePack2.Roles = "b[8] u32 u8 s"; })(RoleManagerPipePack || (RoleManagerPipePack = {})); var RoleManagerEvent = /* @__PURE__ */ ((RoleManagerEvent2) => { RoleManagerEvent2[RoleManagerEvent2["Change"] = 3] = "Change"; return RoleManagerEvent2; })(RoleManagerEvent || {}); var SRV_ROS = 354743340; var RosCmd = /* @__PURE__ */ ((RosCmd2) => { RosCmd2[RosCmd2["PublishMessage"] = 129] = "PublishMessage"; RosCmd2[RosCmd2["SubscribeMessage"] = 130] = "SubscribeMessage"; RosCmd2[RosCmd2["Message"] = 131] = "Message"; return RosCmd2; })(RosCmd || {}); var RosCmdPack; ((RosCmdPack2) => { RosCmdPack2.PublishMessage = "z z s"; RosCmdPack2.SubscribeMessage = "z s"; RosCmdPack2.Message = "z z s"; })(RosCmdPack || (RosCmdPack = {})); var SRV_ROTARY_ENCODER = 284830153; var RotaryEncoderReg = /* @__PURE__ */ ((RotaryEncoderReg2) => { RotaryEncoderReg2[RotaryEncoderReg2["Position"] = 257] = "Position"; RotaryEncoderReg2[RotaryEncoderReg2["ClicksPerTurn"] = 384] = "ClicksPerTurn"; RotaryEncoderReg2[RotaryEncoderReg2["Clicker"] = 385] = "Clicker"; return RotaryEncoderReg2; })(RotaryEncoderReg || {}); var RotaryEncoderRegPack; ((RotaryEncoderRegPack2) => { RotaryEncoderRegPack2.Position = "i32"; RotaryEncoderRegPack2.ClicksPerTurn = "u16"; RotaryEncoderRegPack2.Clicker = "u8"; })(RotaryEncoderRegPack || (RotaryEncoderRegPack = {})); var SRV_ROVER = 435474539; var RoverReg = /* @__PURE__ */ ((RoverReg2) => { RoverReg2[RoverReg2["Kinematics"] = 257] = "Kinematics"; return RoverReg2; })(RoverReg || {}); var RoverRegPack; ((RoverRegPack2) => { RoverRegPack2.Kinematics = "i16.16 i16.16 i16.16 i16.16 i16.16"; })(RoverRegPack || (RoverRegPack = {})); var SRV_SAT_NAV = 433938742; var SatNavReg = /* @__PURE__ */ ((SatNavReg2) => { SatNavReg2[SatNavReg2["Position"] = 257] = "Position"; SatNavReg2[SatNavReg2["Enabled"] = 1] = "Enabled"; return SatNavReg2; })(SatNavReg || {}); var SatNavRegPack; ((SatNavRegPack2) => { SatNavRegPack2.Position = "u64 i9.23 i9.23 u16.16 i26.6 u16.16"; SatNavRegPack2.Enabled = "u8"; })(SatNavRegPack || (SatNavRegPack = {})); var SatNavEvent = /* @__PURE__ */ ((SatNavEvent2) => { SatNavEvent2[SatNavEvent2["Inactive"] = 2] = "Inactive"; return SatNavEvent2; })(SatNavEvent || {}); var SRV_SENSOR_AGGREGATOR = 496034245; var SensorAggregatorSampleType = /* @__PURE__ */ ((SensorAggregatorSampleType2) => { SensorAggregatorSampleType2[SensorAggregatorSampleType2["U8"] = 8] = "U8"; SensorAggregatorSampleType2[SensorAggregatorSampleType2["I8"] = 136] = "I8"; SensorAggregatorSampleType2[SensorAggregatorSampleType2["U16"] = 16] = "U16"; SensorAggregatorSampleType2[SensorAggregatorSampleType2["I16"] = 144] = "I16"; SensorAggregatorSampleType2[SensorAggregatorSampleType2["U32"] = 32] = "U32"; SensorAggregatorSampleType2[SensorAggregatorSampleType2["I32"] = 160] = "I32"; return SensorAggregatorSampleType2; })(SensorAggregatorSampleType || {}); var SensorAggregatorReg = /* @__PURE__ */ ((SensorAggregatorReg2) => { SensorAggregatorReg2[SensorAggregatorReg2["Inputs"] = 128] = "Inputs"; SensorAggregatorReg2[SensorAggregatorReg2["NumSamples"] = 384] = "NumSamples"; SensorAggregatorReg2[SensorAggregatorReg2["SampleSize"] = 385] = "SampleSize"; SensorAggregatorReg2[SensorAggregatorReg2["StreamingSamples"] = 129] = "StreamingSamples"; SensorAggregatorReg2[SensorAggregatorReg2["CurrentSample"] = 257] = "CurrentSample"; return SensorAggregatorReg2; })(SensorAggregatorReg || {}); var SensorAggregatorRegPack; ((SensorAggregatorRegPack2) => { SensorAggregatorRegPack2.Inputs = "u16 u16 u32 r: b[8] u32 u8 u8 u8 i8"; SensorAggregatorRegPack2.NumSamples = "u32"; SensorAggregatorRegPack2.SampleSize = "u8"; SensorAggregatorRegPack2.StreamingSamples = "u32"; SensorAggregatorRegPack2.CurrentSample = "b"; })(SensorAggregatorRegPack || (SensorAggregatorRegPack = {})); var SRV_SERIAL = 297461188; var SerialParityType = /* @__PURE__ */ ((SerialParityType2) => { SerialParityType2[SerialParityType2["None"] = 0] = "None"; SerialParityType2[SerialParityType2["Even"] = 1] = "Even"; SerialParityType2[SerialParityType2["Odd"] = 2] = "Odd"; return SerialParityType2; })(SerialParityType || {}); var SerialReg = /* @__PURE__ */ ((SerialReg2) => { SerialReg2[SerialReg2["Connected"] = 1] = "Connected"; SerialReg2[SerialReg2["ConnectionName"] = 385] = "ConnectionName"; SerialReg2[SerialReg2["BaudRate"] = 128] = "BaudRate"; SerialReg2[SerialReg2["DataBits"] = 129] = "DataBits"; SerialReg2[SerialReg2["StopBits"] = 130] = "StopBits"; SerialReg2[SerialReg2["ParityMode"] = 131] = "ParityMode"; SerialReg2[SerialReg2["BufferSize"] = 132] = "BufferSize"; return SerialReg2; })(SerialReg || {}); var SerialRegPack; ((SerialRegPack2) => { SerialRegPack2.Connected = "u8"; SerialRegPack2.ConnectionName = "s"; SerialRegPack2.BaudRate = "u32"; SerialRegPack2.DataBits = "u8"; SerialRegPack2.StopBits = "u8"; SerialRegPack2.ParityMode = "u8"; SerialRegPack2.BufferSize = "u8"; })(SerialRegPack || (SerialRegPack = {})); var SerialCmd = /* @__PURE__ */ ((SerialCmd2) => { SerialCmd2[SerialCmd2["Send"] = 128] = "Send"; SerialCmd2[SerialCmd2["Received"] = 128] = "Received"; return SerialCmd2; })(SerialCmd || {}); var SerialCmdPack; ((SerialCmdPack2) => { SerialCmdPack2.Send = "b"; SerialCmdPack2.Received = "b"; })(SerialCmdPack || (SerialCmdPack = {})); var SRV_SERVO = 318542083; var ServoReg = /* @__PURE__ */ ((ServoReg2) => { ServoReg2[ServoReg2["Angle"] = 2] = "Angle"; ServoReg2[ServoReg2["Enabled"] = 1] = "Enabled"; ServoReg2[ServoReg2["Offset"] = 129] = "Offset"; ServoReg2[ServoReg2["MinAngle"] = 272] = "MinAngle"; ServoReg2[ServoReg2["MinPulse"] = 131] = "MinPulse"; ServoReg2[ServoReg2["MaxAngle"] = 273] = "MaxAngle"; ServoReg2[ServoReg2["MaxPulse"] = 133] = "MaxPulse"; ServoReg2[ServoReg2["StallTorque"] = 384] = "StallTorque"; ServoReg2[ServoReg2["ResponseSpeed"] = 385] = "ResponseSpeed"; ServoReg2[ServoReg2["ActualAngle"] = 257] = "ActualAngle"; return ServoReg2; })(ServoReg || {}); var ServoRegPack; ((ServoRegPack2) => { ServoRegPack2.Angle = "i16.16"; ServoRegPack2.Enabled = "u8"; ServoRegPack2.Offset = "i16.16"; ServoRegPack2.MinAngle = "i16.16"; ServoRegPack2.MinPulse = "u16"; ServoRegPack2.MaxAngle = "i16.16"; ServoRegPack2.MaxPulse = "u16"; ServoRegPack2.StallTorque = "u16.16"; ServoRegPack2.ResponseSpeed = "u16.16"; ServoRegPack2.ActualAngle = "i16.16"; })(ServoRegPack || (ServoRegPack = {})); var SRV_SETTINGS = 285727818; var SettingsCmd = /* @__PURE__ */ ((SettingsCmd2) => { SettingsCmd2[SettingsCmd2["Get"] = 128] = "Get"; SettingsCmd2[SettingsCmd2["Set"] = 129] = "Set"; SettingsCmd2[SettingsCmd2["Delete"] = 132] = "Delete"; SettingsCmd2[SettingsCmd2["ListKeys"] = 130] = "ListKeys"; SettingsCmd2[SettingsCmd2["List"] = 131] = "List"; SettingsCmd2[SettingsCmd2["Clear"] = 133] = "Clear"; return SettingsCmd2; })(SettingsCmd || {}); var SettingsCmdPack; ((SettingsCmdPack2) => { SettingsCmdPack2.Get = "s"; SettingsCmdPack2.GetReport = "z b"; SettingsCmdPack2.Set = "z b"; SettingsCmdPack2.Delete = "s"; SettingsCmdPack2.ListKeys = "b[12]"; SettingsCmdPack2.List = "b[12]"; })(SettingsCmdPack || (SettingsCmdPack = {})); var SettingsPipe = /* @__PURE__ */ ((SettingsPipe2) => { })(SettingsPipe || {}); var SettingsPipePack; ((SettingsPipePack2) => { SettingsPipePack2.ListedKey = "s"; SettingsPipePack2.ListedEntry = "z b"; })(SettingsPipePack || (SettingsPipePack = {})); var SettingsEvent = /* @__PURE__ */ ((SettingsEvent2) => { SettingsEvent2[SettingsEvent2["Change"] = 3] = "Change"; return SettingsEvent2; })(SettingsEvent || {}); var SRV_SEVEN_SEGMENT_DISPLAY = 425810167; var SevenSegmentDisplayReg = /* @__PURE__ */ ((SevenSegmentDisplayReg3) => { SevenSegmentDisplayReg3[SevenSegmentDisplayReg3["Digits"] = 2] = "Digits"; SevenSegmentDisplayReg3[SevenSegmentDisplayReg3["Brightness"] = 1] = "Brightness"; SevenSegmentDisplayReg3[SevenSegmentDisplayReg3["DoubleDots"] = 128] = "DoubleDots"; SevenSegmentDisplayReg3[SevenSegmentDisplayReg3["DigitCount"] = 384] = "DigitCount"; SevenSegmentDisplayReg3[SevenSegmentDisplayReg3["DecimalPoint"] = 385] = "DecimalPoint"; return SevenSegmentDisplayReg3; })(SevenSegmentDisplayReg || {}); var SevenSegmentDisplayRegPack; ((SevenSegmentDisplayRegPack2) => { SevenSegmentDisplayRegPack2.Digits = "b"; SevenSegmentDisplayRegPack2.Brightness = "u0.16"; SevenSegmentDisplayRegPack2.DoubleDots = "u8"; SevenSegmentDisplayRegPack2.DigitCount = "u8"; SevenSegmentDisplayRegPack2.DecimalPoint = "u8"; })(SevenSegmentDisplayRegPack || (SevenSegmentDisplayRegPack = {})); var SevenSegmentDisplayCmd = /* @__PURE__ */ ((SevenSegmentDisplayCmd2) => { SevenSegmentDisplayCmd2[SevenSegmentDisplayCmd2["SetNumber"] = 128] = "SetNumber"; return SevenSegmentDisplayCmd2; })(SevenSegmentDisplayCmd || {}); var SevenSegmentDisplayCmdPack; ((SevenSegmentDisplayCmdPack2) => { SevenSegmentDisplayCmdPack2.SetNumber = "f64"; })(SevenSegmentDisplayCmdPack || (SevenSegmentDisplayCmdPack = {})); var SRV_SOIL_MOISTURE = 491430835; var SoilMoistureVariant = /* @__PURE__ */ ((SoilMoistureVariant2) => { SoilMoistureVariant2[SoilMoistureVariant2["Resistive"] = 1] = "Resistive"; SoilMoistureVariant2[SoilMoistureVariant2["Capacitive"] = 2] = "Capacitive"; return SoilMoistureVariant2; })(SoilMoistureVariant || {}); var SoilMoistureReg = /* @__PURE__ */ ((SoilMoistureReg2) => { SoilMoistureReg2[SoilMoistureReg2["Moisture"] = 257] = "Moisture"; SoilMoistureReg2[SoilMoistureReg2["MoistureError"] = 262] = "MoistureError"; SoilMoistureReg2[SoilMoistureReg2["Variant"] = 263] = "Variant"; return SoilMoistureReg2; })(SoilMoistureReg || {}); var SoilMoistureRegPack; ((SoilMoistureRegPack2) => { SoilMoistureRegPack2.Moisture = "u0.16"; SoilMoistureRegPack2.MoistureError = "u0.16"; SoilMoistureRegPack2.Variant = "u8"; })(SoilMoistureRegPack || (SoilMoistureRegPack = {})); var SRV_SOLENOID = 387392458; var SolenoidVariant = /* @__PURE__ */ ((SolenoidVariant2) => { SolenoidVariant2[SolenoidVariant2["PushPull"] = 1] = "PushPull"; SolenoidVariant2[SolenoidVariant2["Valve"] = 2] = "Valve"; SolenoidVariant2[SolenoidVariant2["Latch"] = 3] = "Latch"; return SolenoidVariant2; })(SolenoidVariant || {}); var SolenoidReg = /* @__PURE__ */ ((SolenoidReg2) => { SolenoidReg2[SolenoidReg2["Pulled"] = 1] = "Pulled"; SolenoidReg2[SolenoidReg2["Variant"] = 263] = "Variant"; return SolenoidReg2; })(SolenoidReg || {}); var SolenoidRegPack; ((SolenoidRegPack2) => { SolenoidRegPack2.Pulled = "u8"; SolenoidRegPack2.Variant = "u8"; })(SolenoidRegPack || (SolenoidRegPack = {})); var SRV_SOUND_LEVEL = 346888797; var SoundLevelReg = /* @__PURE__ */ ((SoundLevelReg2) => { SoundLevelReg2[SoundLevelReg2["SoundLevel"] = 257] = "SoundLevel"; SoundLevelReg2[SoundLevelReg2["Enabled"] = 1] = "Enabled"; SoundLevelReg2[SoundLevelReg2["LoudThreshold"] = 6] = "LoudThreshold"; SoundLevelReg2[SoundLevelReg2["QuietThreshold"] = 5] = "QuietThreshold"; return SoundLevelReg2; })(SoundLevelReg || {}); var SoundLevelRegPack; ((SoundLevelRegPack2) => { SoundLevelRegPack2.SoundLevel = "u0.16"; SoundLevelRegPack2.Enabled = "u8"; SoundLevelRegPack2.LoudThreshold = "u0.16"; SoundLevelRegPack2.QuietThreshold = "u0.16"; })(SoundLevelRegPack || (SoundLevelRegPack = {})); var SoundLevelEvent = /* @__PURE__ */ ((SoundLevelEvent2) => { SoundLevelEvent2[SoundLevelEvent2["Loud"] = 1] = "Loud"; SoundLevelEvent2[SoundLevelEvent2["Quiet"] = 2] = "Quiet"; return SoundLevelEvent2; })(SoundLevelEvent || {}); var SRV_SOUND_PLAYER = 335795e3; var SoundPlayerReg = /* @__PURE__ */ ((SoundPlayerReg2) => { SoundPlayerReg2[SoundPlayerReg2["Volume"] = 1] = "Volume"; return SoundPlayerReg2; })(SoundPlayerReg || {}); var SoundPlayerRegPack; ((SoundPlayerRegPack2) => { SoundPlayerRegPack2.Volume = "u0.16"; })(SoundPlayerRegPack || (SoundPlayerRegPack = {})); var SoundPlayerCmd = /* @__PURE__ */ ((SoundPlayerCmd2) => { SoundPlayerCmd2[SoundPlayerCmd2["Play"] = 128] = "Play"; SoundPlayerCmd2[SoundPlayerCmd2["Cancel"] = 129] = "Cancel"; SoundPlayerCmd2[SoundPlayerCmd2["ListSounds"] = 130] = "ListSounds"; return SoundPlayerCmd2; })(SoundPlayerCmd || {}); var SoundPlayerCmdPack; ((SoundPlayerCmdPack2) => { SoundPlayerCmdPack2.Play = "s"; SoundPlayerCmdPack2.ListSounds = "b[12]"; })(SoundPlayerCmdPack || (SoundPlayerCmdPack = {})); var SoundPlayerPipe = /* @__PURE__ */ ((SoundPlayerPipe2) => { })(SoundPlayerPipe || {}); var SoundPlayerPipePack; ((SoundPlayerPipePack2) => { SoundPlayerPipePack2.ListSoundsPipe = "u32 s"; })(SoundPlayerPipePack || (SoundPlayerPipePack = {})); var SRV_SOUND_RECORDER_WITH_PLAYBACK = 460504912; var SoundRecorderWithPlaybackStatus = /* @__PURE__ */ ((SoundRecorderWithPlaybackStatus2) => { SoundRecorderWithPlaybackStatus2[SoundRecorderWithPlaybackStatus2["Idle"] = 0] = "Idle"; SoundRecorderWithPlaybackStatus2[SoundRecorderWithPlaybackStatus2["Recording"] = 1] = "Recording"; SoundRecorderWithPlaybackStatus2[SoundRecorderWithPlaybackStatus2["Playing"] = 2] = "Playing"; return SoundRecorderWithPlaybackStatus2; })(SoundRecorderWithPlaybackStatus || {}); var SoundRecorderWithPlaybackCmd = /* @__PURE__ */ ((SoundRecorderWithPlaybackCmd2) => { SoundRecorderWithPlaybackCmd2[SoundRecorderWithPlaybackCmd2["Play"] = 128] = "Play"; SoundRecorderWithPlaybackCmd2[SoundRecorderWithPlaybackCmd2["Record"] = 129] = "Record"; SoundRecorderWithPlaybackCmd2[SoundRecorderWithPlaybackCmd2["Cancel"] = 130] = "Cancel"; return SoundRecorderWithPlaybackCmd2; })(SoundRecorderWithPlaybackCmd || {}); var SoundRecorderWithPlaybackCmdPack; ((SoundRecorderWithPlaybackCmdPack2) => { SoundRecorderWithPlaybackCmdPack2.Record = "u16"; })(SoundRecorderWithPlaybackCmdPack || (SoundRecorderWithPlaybackCmdPack = {})); var SoundRecorderWithPlaybackReg = /* @__PURE__ */ ((SoundRecorderWithPlaybackReg2) => { SoundRecorderWithPlaybackReg2[SoundRecorderWithPlaybackReg2["Status"] = 384] = "Status"; SoundRecorderWithPlaybackReg2[SoundRecorderWithPlaybackReg2["Time"] = 385] = "Time"; SoundRecorderWithPlaybackReg2[SoundRecorderWithPlaybackReg2["Volume"] = 1] = "Volume"; return SoundRecorderWithPlaybackReg2; })(SoundRecorderWithPlaybackReg || {}); var SoundRecorderWithPlaybackRegPack; ((SoundRecorderWithPlaybackRegPack2) => { SoundRecorderWithPlaybackRegPack2.Status = "u8"; SoundRecorderWithPlaybackRegPack2.Time = "u16"; SoundRecorderWithPlaybackRegPack2.Volume = "u0.8"; })(SoundRecorderWithPlaybackRegPack || (SoundRecorderWithPlaybackRegPack = {})); var SRV_SOUND_SPECTRUM = 360365086; var SoundSpectrumReg = /* @__PURE__ */ ((SoundSpectrumReg2) => { SoundSpectrumReg2[SoundSpectrumReg2["FrequencyBins"] = 257] = "FrequencyBins"; SoundSpectrumReg2[SoundSpectrumReg2["Enabled"] = 1] = "Enabled"; SoundSpectrumReg2[SoundSpectrumReg2["FftPow2Size"] = 128] = "FftPow2Size"; SoundSpectrumReg2[SoundSpectrumReg2["MinDecibels"] = 129] = "MinDecibels"; SoundSpectrumReg2[SoundSpectrumReg2["MaxDecibels"] = 130] = "MaxDecibels"; SoundSpectrumReg2[SoundSpectrumReg2["SmoothingTimeConstant"] = 131] = "SmoothingTimeConstant"; return SoundSpectrumReg2; })(SoundSpectrumReg || {}); var SoundSpectrumRegPack; ((SoundSpectrumRegPack2) => { SoundSpectrumRegPack2.FrequencyBins = "b"; SoundSpectrumRegPack2.Enabled = "u8"; SoundSpectrumRegPack2.FftPow2Size = "u8"; SoundSpectrumRegPack2.MinDecibels = "i16"; SoundSpectrumRegPack2.MaxDecibels = "i16"; SoundSpectrumRegPack2.SmoothingTimeConstant = "u0.8"; })(SoundSpectrumRegPack || (SoundSpectrumRegPack = {})); var SRV_SPEECH_SYNTHESIS = 302307733; var SpeechSynthesisReg = /* @__PURE__ */ ((SpeechSynthesisReg2) => { SpeechSynthesisReg2[SpeechSynthesisReg2["Enabled"] = 1] = "Enabled"; SpeechSynthesisReg2[SpeechSynthesisReg2["Lang"] = 128] = "Lang"; SpeechSynthesisReg2[SpeechSynthesisReg2["Volume"] = 129] = "Volume"; SpeechSynthesisReg2[SpeechSynthesisReg2["Pitch"] = 130] = "Pitch"; SpeechSynthesisReg2[SpeechSynthesisReg2["Rate"] = 131] = "Rate"; return SpeechSynthesisReg2; })(SpeechSynthesisReg || {}); var SpeechSynthesisRegPack; ((SpeechSynthesisRegPack2) => { SpeechSynthesisRegPack2.Enabled = "u8"; SpeechSynthesisRegPack2.Lang = "s"; SpeechSynthesisRegPack2.Volume = "u0.8"; SpeechSynthesisRegPack2.Pitch = "u16.16"; SpeechSynthesisRegPack2.Rate = "u16.16"; })(SpeechSynthesisRegPack || (SpeechSynthesisRegPack = {})); var SpeechSynthesisCmd = /* @__PURE__ */ ((SpeechSynthesisCmd2) => { SpeechSynthesisCmd2[SpeechSynthesisCmd2["Speak"] = 128] = "Speak"; SpeechSynthesisCmd2[SpeechSynthesisCmd2["Cancel"] = 129] = "Cancel"; return SpeechSynthesisCmd2; })(SpeechSynthesisCmd || {}); var SpeechSynthesisCmdPack; ((SpeechSynthesisCmdPack2) => { SpeechSynthesisCmdPack2.Speak = "s"; })(SpeechSynthesisCmdPack || (SpeechSynthesisCmdPack = {})); var SRV_SWITCH = 450008066; var SwitchVariant = /* @__PURE__ */ ((SwitchVariant3) => { SwitchVariant3[SwitchVariant3["Slide"] = 1] = "Slide"; SwitchVariant3[SwitchVariant3["Tilt"] = 2] = "Tilt"; SwitchVariant3[SwitchVariant3["PushButton"] = 3] = "PushButton"; SwitchVariant3[SwitchVariant3["Tactile"] = 4] = "Tactile"; SwitchVariant3[SwitchVariant3["Toggle"] = 5] = "Toggle"; SwitchVariant3[SwitchVariant3["Proximity"] = 6] = "Proximity"; SwitchVariant3[SwitchVariant3["Magnetic"] = 7] = "Magnetic"; SwitchVariant3[SwitchVariant3["FootButton"] = 8] = "FootButton"; return SwitchVariant3; })(SwitchVariant || {}); var SwitchReg = /* @__PURE__ */ ((SwitchReg2) => { SwitchReg2[SwitchReg2["Active"] = 257] = "Active"; SwitchReg2[SwitchReg2["Variant"] = 263] = "Variant"; return SwitchReg2; })(SwitchReg || {}); var SwitchRegPack; ((SwitchRegPack2) => { SwitchRegPack2.Active = "u8"; SwitchRegPack2.Variant = "u8"; })(SwitchRegPack || (SwitchRegPack = {})); var SwitchEvent = /* @__PURE__ */ ((SwitchEvent2) => { SwitchEvent2[SwitchEvent2["On"] = 1] = "On"; SwitchEvent2[SwitchEvent2["Off"] = 2] = "Off"; return SwitchEvent2; })(SwitchEvent || {}); var SRV_TCP = 457422603; var TcpTcpError = /* @__PURE__ */ ((TcpTcpError2) => { TcpTcpError2[TcpTcpError2["InvalidCommand"] = 1] = "InvalidCommand"; TcpTcpError2[TcpTcpError2["InvalidCommandPayload"] = 2] = "InvalidCommandPayload"; return TcpTcpError2; })(TcpTcpError || {}); var TcpCmd = /* @__PURE__ */ ((TcpCmd2) => { TcpCmd2[TcpCmd2["Open"] = 128] = "Open"; return TcpCmd2; })(TcpCmd || {}); var TcpCmdPack; ((TcpCmdPack2) => { TcpCmdPack2.Open = "b[12]"; TcpCmdPack2.OpenReport = "u16"; })(TcpCmdPack || (TcpCmdPack = {})); var TcpPipeCmd = /* @__PURE__ */ ((TcpPipeCmd2) => { TcpPipeCmd2[TcpPipeCmd2["OpenSsl"] = 1] = "OpenSsl"; TcpPipeCmd2[TcpPipeCmd2["Error"] = 0] = "Error"; return TcpPipeCmd2; })(TcpPipeCmd || {}); var TcpPipeCmdPack; ((TcpPipeCmdPack2) => { TcpPipeCmdPack2.OpenSsl = "u16 s"; TcpPipeCmdPack2.Error = "i32"; })(TcpPipeCmdPack || (TcpPipeCmdPack = {})); var TcpPipe = /* @__PURE__ */ ((TcpPipe2) => { })(TcpPipe || {}); var TcpPipePack; ((TcpPipePack2) => { TcpPipePack2.Outdata = "b"; TcpPipePack2.Indata = "b"; })(TcpPipePack || (TcpPipePack = {})); var SRV_TEMPERATURE = 337754823; var TemperatureVariant = /* @__PURE__ */ ((TemperatureVariant2) => { TemperatureVariant2[TemperatureVariant2["Outdoor"] = 1] = "Outdoor"; TemperatureVariant2[TemperatureVariant2["Indoor"] = 2] = "Indoor"; TemperatureVariant2[TemperatureVariant2["Body"] = 3] = "Body"; return TemperatureVariant2; })(TemperatureVariant || {}); var TemperatureReg = /* @__PURE__ */ ((TemperatureReg2) => { TemperatureReg2[TemperatureReg2["Temperature"] = 257] = "Temperature"; TemperatureReg2[TemperatureReg2["MinTemperature"] = 260] = "MinTemperature"; TemperatureReg2[TemperatureReg2["MaxTemperature"] = 261] = "MaxTemperature"; TemperatureReg2[TemperatureReg2["TemperatureError"] = 262] = "TemperatureError"; TemperatureReg2[TemperatureReg2["Variant"] = 263] = "Variant"; return TemperatureReg2; })(TemperatureReg || {}); var TemperatureRegPack; ((TemperatureRegPack2) => { TemperatureRegPack2.Temperature = "i22.10"; TemperatureRegPack2.MinTemperature = "i22.10"; TemperatureRegPack2.MaxTemperature = "i22.10"; TemperatureRegPack2.TemperatureError = "u22.10"; TemperatureRegPack2.Variant = "u8"; })(TemperatureRegPack || (TemperatureRegPack = {})); var SRV_TIMESERIES_AGGREGATOR = 294829516; var TimeseriesAggregatorCmd = /* @__PURE__ */ ((TimeseriesAggregatorCmd2) => { TimeseriesAggregatorCmd2[TimeseriesAggregatorCmd2["Clear"] = 128] = "Clear"; TimeseriesAggregatorCmd2[TimeseriesAggregatorCmd2["Update"] = 131] = "Update"; TimeseriesAggregatorCmd2[TimeseriesAggregatorCmd2["SetWindow"] = 132] = "SetWindow"; TimeseriesAggregatorCmd2[TimeseriesAggregatorCmd2["SetUpload"] = 133] = "SetUpload"; TimeseriesAggregatorCmd2[TimeseriesAggregatorCmd2["Stored"] = 144] = "Stored"; return TimeseriesAggregatorCmd2; })(TimeseriesAggregatorCmd || {}); var TimeseriesAggregatorCmdPack; ((TimeseriesAggregatorCmdPack2) => { TimeseriesAggregatorCmdPack2.Update = "f64 s"; TimeseriesAggregatorCmdPack2.SetWindow = "u32 s"; TimeseriesAggregatorCmdPack2.SetUpload = "u8 s"; TimeseriesAggregatorCmdPack2.Stored = "u32 b[4] f64 f64 f64 u32 u32 s"; })(TimeseriesAggregatorCmdPack || (TimeseriesAggregatorCmdPack = {})); var TimeseriesAggregatorReg = /* @__PURE__ */ ((TimeseriesAggregatorReg2) => { TimeseriesAggregatorReg2[TimeseriesAggregatorReg2["Now"] = 384] = "Now"; TimeseriesAggregatorReg2[TimeseriesAggregatorReg2["FastStart"] = 128] = "FastStart"; TimeseriesAggregatorReg2[TimeseriesAggregatorReg2["DefaultWindow"] = 129] = "DefaultWindow"; TimeseriesAggregatorReg2[TimeseriesAggregatorReg2["DefaultUpload"] = 130] = "DefaultUpload"; TimeseriesAggregatorReg2[TimeseriesAggregatorReg2["UploadUnlabelled"] = 131] = "UploadUnlabelled"; TimeseriesAggregatorReg2[TimeseriesAggregatorReg2["SensorWatchdogPeriod"] = 132] = "SensorWatchdogPeriod"; return TimeseriesAggregatorReg2; })(TimeseriesAggregatorReg || {}); var TimeseriesAggregatorRegPack; ((TimeseriesAggregatorRegPack2) => { TimeseriesAggregatorRegPack2.Now = "u32"; TimeseriesAggregatorRegPack2.FastStart = "u8"; TimeseriesAggregatorRegPack2.DefaultWindow = "u32"; TimeseriesAggregatorRegPack2.DefaultUpload = "u8"; TimeseriesAggregatorRegPack2.UploadUnlabelled = "u8"; TimeseriesAggregatorRegPack2.SensorWatchdogPeriod = "u32"; })(TimeseriesAggregatorRegPack || (TimeseriesAggregatorRegPack = {})); var SRV_TRAFFIC_LIGHT = 365137307; var TrafficLightReg = /* @__PURE__ */ ((TrafficLightReg2) => { TrafficLightReg2[TrafficLightReg2["Red"] = 128] = "Red"; TrafficLightReg2[TrafficLightReg2["Yellow"] = 129] = "Yellow"; TrafficLightReg2[TrafficLightReg2["Green"] = 130] = "Green"; return TrafficLightReg2; })(TrafficLightReg || {}); var TrafficLightRegPack; ((TrafficLightRegPack2) => { TrafficLightRegPack2.Red = "u8"; TrafficLightRegPack2.Yellow = "u8"; TrafficLightRegPack2.Green = "u8"; })(TrafficLightRegPack || (TrafficLightRegPack = {})); var SRV_TVOC = 312849815; var TvocReg = /* @__PURE__ */ ((TvocReg2) => { TvocReg2[TvocReg2["TVOC"] = 257] = "TVOC"; TvocReg2[TvocReg2["TVOCError"] = 262] = "TVOCError"; TvocReg2[TvocReg2["MinTVOC"] = 260] = "MinTVOC"; TvocReg2[TvocReg2["MaxTVOC"] = 261] = "MaxTVOC"; return TvocReg2; })(TvocReg || {}); var TvocRegPack; ((TvocRegPack2) => { TvocRegPack2.TVOC = "u22.10"; TvocRegPack2.TVOCError = "u22.10"; TvocRegPack2.MinTVOC = "u22.10"; TvocRegPack2.MaxTVOC = "u22.10"; })(TvocRegPack || (TvocRegPack = {})); var SRV_UNIQUE_BRAIN = 272387813; var SRV_USB_BRIDGE = 418781770; var UsbBridgeQByte = /* @__PURE__ */ ((UsbBridgeQByte2) => { UsbBridgeQByte2[UsbBridgeQByte2["Magic"] = 254] = "Magic"; UsbBridgeQByte2[UsbBridgeQByte2["LiteralMagic"] = 248] = "LiteralMagic"; UsbBridgeQByte2[UsbBridgeQByte2["Reserved"] = 249] = "Reserved"; UsbBridgeQByte2[UsbBridgeQByte2["SerialGap"] = 250] = "SerialGap"; UsbBridgeQByte2[UsbBridgeQByte2["FrameGap"] = 251] = "FrameGap"; UsbBridgeQByte2[UsbBridgeQByte2["FrameStart"] = 252] = "FrameStart"; UsbBridgeQByte2[UsbBridgeQByte2["FrameEnd"] = 253] = "FrameEnd"; return UsbBridgeQByte2; })(UsbBridgeQByte || {}); var UsbBridgeCmd = /* @__PURE__ */ ((UsbBridgeCmd2) => { UsbBridgeCmd2[UsbBridgeCmd2["DisablePackets"] = 128] = "DisablePackets"; UsbBridgeCmd2[UsbBridgeCmd2["EnablePackets"] = 129] = "EnablePackets"; UsbBridgeCmd2[UsbBridgeCmd2["DisableLog"] = 130] = "DisableLog"; UsbBridgeCmd2[UsbBridgeCmd2["EnableLog"] = 131] = "EnableLog"; return UsbBridgeCmd2; })(UsbBridgeCmd || {}); var SRV_UV_INDEX = 527306128; var UvIndexVariant = /* @__PURE__ */ ((UvIndexVariant2) => { UvIndexVariant2[UvIndexVariant2["UVA_UVB"] = 1] = "UVA_UVB"; UvIndexVariant2[UvIndexVariant2["Visible_IR"] = 2] = "Visible_IR"; return UvIndexVariant2; })(UvIndexVariant || {}); var UvIndexReg = /* @__PURE__ */ ((UvIndexReg2) => { UvIndexReg2[UvIndexReg2["UvIndex"] = 257] = "UvIndex"; UvIndexReg2[UvIndexReg2["UvIndexError"] = 262] = "UvIndexError"; UvIndexReg2[UvIndexReg2["Variant"] = 263] = "Variant"; return UvIndexReg2; })(UvIndexReg || {}); var UvIndexRegPack; ((UvIndexRegPack2) => { UvIndexRegPack2.UvIndex = "u16.16"; UvIndexRegPack2.UvIndexError = "u16.16"; UvIndexRegPack2.Variant = "u8"; })(UvIndexRegPack || (UvIndexRegPack = {})); var SRV_VERIFIED_TELEMETRY = 563381279; var VerifiedTelemetryStatus = /* @__PURE__ */ ((VerifiedTelemetryStatus2) => { VerifiedTelemetryStatus2[VerifiedTelemetryStatus2["Unknown"] = 0] = "Unknown"; VerifiedTelemetryStatus2[VerifiedTelemetryStatus2["Working"] = 1] = "Working"; VerifiedTelemetryStatus2[VerifiedTelemetryStatus2["Faulty"] = 2] = "Faulty"; return VerifiedTelemetryStatus2; })(VerifiedTelemetryStatus || {}); var VerifiedTelemetryFingerprintType = /* @__PURE__ */ ((VerifiedTelemetryFingerprintType2) => { VerifiedTelemetryFingerprintType2[VerifiedTelemetryFingerprintType2["FallCurve"] = 1] = "FallCurve"; VerifiedTelemetryFingerprintType2[VerifiedTelemetryFingerprintType2["CurrentSense"] = 2] = "CurrentSense"; VerifiedTelemetryFingerprintType2[VerifiedTelemetryFingerprintType2["Custom"] = 3] = "Custom"; return VerifiedTelemetryFingerprintType2; })(VerifiedTelemetryFingerprintType || {}); var VerifiedTelemetryReg = /* @__PURE__ */ ((VerifiedTelemetryReg2) => { VerifiedTelemetryReg2[VerifiedTelemetryReg2["TelemetryStatus"] = 384] = "TelemetryStatus"; VerifiedTelemetryReg2[VerifiedTelemetryReg2["TelemetryStatusInterval"] = 128] = "TelemetryStatusInterval"; VerifiedTelemetryReg2[VerifiedTelemetryReg2["FingerprintType"] = 385] = "FingerprintType"; VerifiedTelemetryReg2[VerifiedTelemetryReg2["FingerprintTemplate"] = 386] = "FingerprintTemplate"; return VerifiedTelemetryReg2; })(VerifiedTelemetryReg || {}); var VerifiedTelemetryRegPack; ((VerifiedTelemetryRegPack2) => { VerifiedTelemetryRegPack2.TelemetryStatus = "u8"; VerifiedTelemetryRegPack2.TelemetryStatusInterval = "u32"; VerifiedTelemetryRegPack2.FingerprintType = "u8"; VerifiedTelemetryRegPack2.FingerprintTemplate = "u16 b"; })(VerifiedTelemetryRegPack || (VerifiedTelemetryRegPack = {})); var VerifiedTelemetryCmd = /* @__PURE__ */ ((VerifiedTelemetryCmd2) => { VerifiedTelemetryCmd2[VerifiedTelemetryCmd2["ResetFingerprintTemplate"] = 128] = "ResetFingerprintTemplate"; VerifiedTelemetryCmd2[VerifiedTelemetryCmd2["RetrainFingerprintTemplate"] = 129] = "RetrainFingerprintTemplate"; return VerifiedTelemetryCmd2; })(VerifiedTelemetryCmd || {}); var VerifiedTelemetryEvent = /* @__PURE__ */ ((VerifiedTelemetryEvent2) => { VerifiedTelemetryEvent2[VerifiedTelemetryEvent2["TelemetryStatusChange"] = 3] = "TelemetryStatusChange"; VerifiedTelemetryEvent2[VerifiedTelemetryEvent2["FingerprintTemplateChange"] = 128] = "FingerprintTemplateChange"; return VerifiedTelemetryEvent2; })(VerifiedTelemetryEvent || {}); var VerifiedTelemetryEventPack; ((VerifiedTelemetryEventPack2) => { VerifiedTelemetryEventPack2.TelemetryStatusChange = "u8"; })(VerifiedTelemetryEventPack || (VerifiedTelemetryEventPack = {})); var SRV_VIBRATION_MOTOR = 406832290; var VibrationMotorCmd = /* @__PURE__ */ ((VibrationMotorCmd2) => { VibrationMotorCmd2[VibrationMotorCmd2["Vibrate"] = 128] = "Vibrate"; return VibrationMotorCmd2; })(VibrationMotorCmd || {}); var VibrationMotorCmdPack; ((VibrationMotorCmdPack2) => { VibrationMotorCmdPack2.Vibrate = "r: u8 u0.8"; })(VibrationMotorCmdPack || (VibrationMotorCmdPack = {})); var VibrationMotorReg = /* @__PURE__ */ ((VibrationMotorReg2) => { VibrationMotorReg2[VibrationMotorReg2["MaxVibrations"] = 384] = "MaxVibrations"; return VibrationMotorReg2; })(VibrationMotorReg || {}); var VibrationMotorRegPack; ((VibrationMotorRegPack2) => { VibrationMotorRegPack2.MaxVibrations = "u8"; })(VibrationMotorRegPack || (VibrationMotorRegPack = {})); var SRV_WATER_LEVEL = 343630573; var WaterLevelVariant = /* @__PURE__ */ ((WaterLevelVariant2) => { WaterLevelVariant2[WaterLevelVariant2["Resistive"] = 1] = "Resistive"; WaterLevelVariant2[WaterLevelVariant2["ContactPhotoElectric"] = 2] = "ContactPhotoElectric"; WaterLevelVariant2[WaterLevelVariant2["NonContactPhotoElectric"] = 3] = "NonContactPhotoElectric"; return WaterLevelVariant2; })(WaterLevelVariant || {}); var WaterLevelReg = /* @__PURE__ */ ((WaterLevelReg2) => { WaterLevelReg2[WaterLevelReg2["Level"] = 257] = "Level"; WaterLevelReg2[WaterLevelReg2["LevelError"] = 262] = "LevelError"; WaterLevelReg2[WaterLevelReg2["Variant"] = 263] = "Variant"; return WaterLevelReg2; })(WaterLevelReg || {}); var WaterLevelRegPack; ((WaterLevelRegPack2) => { WaterLevelRegPack2.Level = "u0.16"; WaterLevelRegPack2.LevelError = "u0.16"; WaterLevelRegPack2.Variant = "u8"; })(WaterLevelRegPack || (WaterLevelRegPack = {})); var SRV_WEIGHT_SCALE = 525160512; var WeightScaleVariant = /* @__PURE__ */ ((WeightScaleVariant2) => { WeightScaleVariant2[WeightScaleVariant2["Body"] = 1] = "Body"; WeightScaleVariant2[WeightScaleVariant2["Food"] = 2] = "Food"; WeightScaleVariant2[WeightScaleVariant2["Jewelry"] = 3] = "Jewelry"; return WeightScaleVariant2; })(WeightScaleVariant || {}); var WeightScaleReg = /* @__PURE__ */ ((WeightScaleReg2) => { WeightScaleReg2[WeightScaleReg2["Weight"] = 257] = "Weight"; WeightScaleReg2[WeightScaleReg2["WeightError"] = 262] = "WeightError"; WeightScaleReg2[WeightScaleReg2["ZeroOffset"] = 128] = "ZeroOffset"; WeightScaleReg2[WeightScaleReg2["Gain"] = 129] = "Gain"; WeightScaleReg2[WeightScaleReg2["MaxWeight"] = 261] = "MaxWeight"; WeightScaleReg2[WeightScaleReg2["MinWeight"] = 260] = "MinWeight"; WeightScaleReg2[WeightScaleReg2["WeightResolution"] = 264] = "WeightResolution"; WeightScaleReg2[WeightScaleReg2["Variant"] = 263] = "Variant"; return WeightScaleReg2; })(WeightScaleReg || {}); var WeightScaleRegPack; ((WeightScaleRegPack2) => { WeightScaleRegPack2.Weight = "u16.16"; WeightScaleRegPack2.WeightError = "u16.16"; WeightScaleRegPack2.ZeroOffset = "u16.16"; WeightScaleRegPack2.Gain = "u16.16"; WeightScaleRegPack2.MaxWeight = "u16.16"; WeightScaleRegPack2.MinWeight = "u16.16"; WeightScaleRegPack2.WeightResolution = "u16.16"; WeightScaleRegPack2.Variant = "u8"; })(WeightScaleRegPack || (WeightScaleRegPack = {})); var WeightScaleCmd = /* @__PURE__ */ ((WeightScaleCmd2) => { WeightScaleCmd2[WeightScaleCmd2["CalibrateZeroOffset"] = 128] = "CalibrateZeroOffset"; WeightScaleCmd2[WeightScaleCmd2["CalibrateGain"] = 129] = "CalibrateGain"; return WeightScaleCmd2; })(WeightScaleCmd || {}); var WeightScaleCmdPack; ((WeightScaleCmdPack2) => { WeightScaleCmdPack2.CalibrateGain = "u22.10"; })(WeightScaleCmdPack || (WeightScaleCmdPack = {})); var SRV_WIFI = 413852154; var WifiAPFlags = /* @__PURE__ */ ((WifiAPFlags3) => { WifiAPFlags3[WifiAPFlags3["HasPassword"] = 1] = "HasPassword"; WifiAPFlags3[WifiAPFlags3["WPS"] = 2] = "WPS"; WifiAPFlags3[WifiAPFlags3["HasSecondaryChannelAbove"] = 4] = "HasSecondaryChannelAbove"; WifiAPFlags3[WifiAPFlags3["HasSecondaryChannelBelow"] = 8] = "HasSecondaryChannelBelow"; WifiAPFlags3[WifiAPFlags3["IEEE_802_11B"] = 256] = "IEEE_802_11B"; WifiAPFlags3[WifiAPFlags3["IEEE_802_11A"] = 512] = "IEEE_802_11A"; WifiAPFlags3[WifiAPFlags3["IEEE_802_11G"] = 1024] = "IEEE_802_11G"; WifiAPFlags3[WifiAPFlags3["IEEE_802_11N"] = 2048] = "IEEE_802_11N"; WifiAPFlags3[WifiAPFlags3["IEEE_802_11AC"] = 4096] = "IEEE_802_11AC"; WifiAPFlags3[WifiAPFlags3["IEEE_802_11AX"] = 8192] = "IEEE_802_11AX"; WifiAPFlags3[WifiAPFlags3["IEEE_802_LongRange"] = 32768] = "IEEE_802_LongRange"; return WifiAPFlags3; })(WifiAPFlags || {}); var WifiCmd = /* @__PURE__ */ ((WifiCmd2) => { WifiCmd2[WifiCmd2["LastScanResults"] = 128] = "LastScanResults"; WifiCmd2[WifiCmd2["AddNetwork"] = 129] = "AddNetwork"; WifiCmd2[WifiCmd2["Reconnect"] = 130] = "Reconnect"; WifiCmd2[WifiCmd2["ForgetNetwork"] = 131] = "ForgetNetwork"; WifiCmd2[WifiCmd2["ForgetAllNetworks"] = 132] = "ForgetAllNetworks"; WifiCmd2[WifiCmd2["SetNetworkPriority"] = 133] = "SetNetworkPriority"; WifiCmd2[WifiCmd2["Scan"] = 134] = "Scan"; WifiCmd2[WifiCmd2["ListKnownNetworks"] = 135] = "ListKnownNetworks"; return WifiCmd2; })(WifiCmd || {}); var WifiCmdPack; ((WifiCmdPack2) => { WifiCmdPack2.LastScanResults = "b[12]"; WifiCmdPack2.AddNetwork = "z z"; WifiCmdPack2.ForgetNetwork = "s"; WifiCmdPack2.SetNetworkPriority = "i16 s"; WifiCmdPack2.ListKnownNetworks = "b[12]"; })(WifiCmdPack || (WifiCmdPack = {})); var WifiPipe = /* @__PURE__ */ ((WifiPipe2) => { })(WifiPipe || {}); var WifiPipePack; ((WifiPipePack2) => { WifiPipePack2.Results = "u32 u32 i8 u8 b[6] s[33]"; WifiPipePack2.NetworkResults = "i16 i16 s"; })(WifiPipePack || (WifiPipePack = {})); var WifiReg = /* @__PURE__ */ ((WifiReg2) => { WifiReg2[WifiReg2["Rssi"] = 257] = "Rssi"; WifiReg2[WifiReg2["Enabled"] = 1] = "Enabled"; WifiReg2[WifiReg2["IpAddress"] = 385] = "IpAddress"; WifiReg2[WifiReg2["Eui48"] = 386] = "Eui48"; WifiReg2[WifiReg2["Ssid"] = 387] = "Ssid"; return WifiReg2; })(WifiReg || {}); var WifiRegPack; ((WifiRegPack2) => { WifiRegPack2.Rssi = "i8"; WifiRegPack2.Enabled = "u8"; WifiRegPack2.IpAddress = "b[16]"; WifiRegPack2.Eui48 = "b[6]"; WifiRegPack2.Ssid = "s[32]"; })(WifiRegPack || (WifiRegPack = {})); var WifiEvent = /* @__PURE__ */ ((WifiEvent2) => { WifiEvent2[WifiEvent2["GotIp"] = 1] = "GotIp"; WifiEvent2[WifiEvent2["LostIp"] = 2] = "LostIp"; WifiEvent2[WifiEvent2["ScanComplete"] = 128] = "ScanComplete"; WifiEvent2[WifiEvent2["NetworksChanged"] = 129] = "NetworksChanged"; WifiEvent2[WifiEvent2["ConnectionFailed"] = 130] = "ConnectionFailed"; return WifiEvent2; })(WifiEvent || {}); var WifiEventPack; ((WifiEventPack2) => { WifiEventPack2.ScanComplete = "u16 u16"; WifiEventPack2.ConnectionFailed = "s"; })(WifiEventPack || (WifiEventPack = {})); var SRV_WIND_DIRECTION = 409725227; var WindDirectionReg = /* @__PURE__ */ ((WindDirectionReg2) => { WindDirectionReg2[WindDirectionReg2["WindDirection"] = 257] = "WindDirection"; WindDirectionReg2[WindDirectionReg2["WindDirectionError"] = 262] = "WindDirectionError"; return WindDirectionReg2; })(WindDirectionReg || {}); var WindDirectionRegPack; ((WindDirectionRegPack2) => { WindDirectionRegPack2.WindDirection = "u16"; WindDirectionRegPack2.WindDirectionError = "u16"; })(WindDirectionRegPack || (WindDirectionRegPack = {})); var SRV_WIND_SPEED = 458824639; var WindSpeedReg = /* @__PURE__ */ ((WindSpeedReg2) => { WindSpeedReg2[WindSpeedReg2["WindSpeed"] = 257] = "WindSpeed"; WindSpeedReg2[WindSpeedReg2["WindSpeedError"] = 262] = "WindSpeedError"; WindSpeedReg2[WindSpeedReg2["MaxWindSpeed"] = 261] = "MaxWindSpeed"; return WindSpeedReg2; })(WindSpeedReg || {}); var WindSpeedRegPack; ((WindSpeedRegPack2) => { WindSpeedRegPack2.WindSpeed = "u16.16"; WindSpeedRegPack2.WindSpeedError = "u16.16"; WindSpeedRegPack2.MaxWindSpeed = "u16.16"; })(WindSpeedRegPack || (WindSpeedRegPack = {})); var SRV_WSSK = 330775038; var WsskStreamingType = /* @__PURE__ */ ((WsskStreamingType2) => { WsskStreamingType2[WsskStreamingType2["Jacdac"] = 1] = "Jacdac"; WsskStreamingType2[WsskStreamingType2["Dmesg"] = 2] = "Dmesg"; WsskStreamingType2[WsskStreamingType2["Exceptions"] = 256] = "Exceptions"; WsskStreamingType2[WsskStreamingType2["TemporaryMask"] = 255] = "TemporaryMask"; WsskStreamingType2[WsskStreamingType2["PermamentMask"] = 65280] = "PermamentMask"; WsskStreamingType2[WsskStreamingType2["DefaultMask"] = 256] = "DefaultMask"; return WsskStreamingType2; })(WsskStreamingType || {}); var WsskDataType = /* @__PURE__ */ ((WsskDataType2) => { WsskDataType2[WsskDataType2["Binary"] = 1] = "Binary"; WsskDataType2[WsskDataType2["JSON"] = 2] = "JSON"; return WsskDataType2; })(WsskDataType || {}); var WsskCmd = /* @__PURE__ */ ((WsskCmd2) => { WsskCmd2[WsskCmd2["Error"] = 255] = "Error"; WsskCmd2[WsskCmd2["SetStreaming"] = 144] = "SetStreaming"; WsskCmd2[WsskCmd2["PingDevice"] = 145] = "PingDevice"; WsskCmd2[WsskCmd2["PingCloud"] = 146] = "PingCloud"; WsskCmd2[WsskCmd2["GetHash"] = 147] = "GetHash"; WsskCmd2[WsskCmd2["DeployStart"] = 148] = "DeployStart"; WsskCmd2[WsskCmd2["DeployWrite"] = 149] = "DeployWrite"; WsskCmd2[WsskCmd2["DeployFinish"] = 150] = "DeployFinish"; WsskCmd2[WsskCmd2["C2d"] = 151] = "C2d"; WsskCmd2[WsskCmd2["D2c"] = 152] = "D2c"; WsskCmd2[WsskCmd2["JacdacPacket"] = 153] = "JacdacPacket"; WsskCmd2[WsskCmd2["Dmesg"] = 154] = "Dmesg"; WsskCmd2[WsskCmd2["ExceptionReport"] = 155] = "ExceptionReport"; return WsskCmd2; })(WsskCmd || {}); var WsskCmdPack; ((WsskCmdPack2) => { WsskCmdPack2.Error = "s"; WsskCmdPack2.SetStreaming = "u16"; WsskCmdPack2.PingDevice = "b"; WsskCmdPack2.PingDeviceReport = "b"; WsskCmdPack2.PingCloud = "b"; WsskCmdPack2.PingCloudReport = "b"; WsskCmdPack2.GetHashReport = "b[32]"; WsskCmdPack2.DeployStart = "u32"; WsskCmdPack2.DeployWrite = "b"; WsskCmdPack2.C2d = "u8 z b"; WsskCmdPack2.D2c = "u8 z b"; WsskCmdPack2.JacdacPacket = "b"; WsskCmdPack2.JacdacPacketReport = "b"; WsskCmdPack2.Dmesg = "b"; WsskCmdPack2.ExceptionReport = "b"; })(WsskCmdPack || (WsskCmdPack = {})); // src/jdom/constants.ts var CMD_GET_REG = 4096; var CMD_SET_REG = 8192; var CMD_EVENT_MASK = 32768; var CMD_EVENT_CODE_MASK = 255; var CMD_EVENT_COUNTER_POS = 8; var CMD_EVENT_COUNTER_MASK = 127; var CMD_TOP_MASK = 61440; var CMD_REG_MASK = 4095; var ACK_MIN_DELAY = 90; var ACK_MAX_DELAY = 120; var CMD_ADVERTISEMENT_DATA = 0; var PIPE_PORT_SHIFT = 7; var PIPE_COUNTER_MASK = 31; var PIPE_CLOSE_MASK = 32; var PIPE_METADATA_MASK = 64; var JD_SERIAL_HEADER_SIZE = 16; var JD_SERIAL_MAX_PAYLOAD_SIZE = 236; var JD_SERVICE_INDEX_MASK = 63; var JD_SERVICE_INDEX_INV_MASK = 192; var JD_SERVICE_INDEX_CRC_ACK = 63; var JD_SERVICE_INDEX_PIPE = 62; var JD_SERVICE_INDEX_MAX_NORMAL = 48; var JD_SERVICE_INDEX_CTRL = 0; var JD_SERVICE_INDEX_BROADCAST = 61; var JD_FRAME_FLAG_COMMAND = 1; var JD_FRAME_FLAG_ACK_REQUESTED = 2; var JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS = 4; var JD_FRAME_FLAG_LOOPBACK = 64; var JD_FRAME_FLAG_VNEXT = 128; var JD_DEVICE_IDENTIFIER_BROADCAST_HIGH_MARK = 2863311530; var JD_ADVERTISEMENT_0_COUNTER_MASK = 15; var JD_ADVERTISEMENT_0_ACK_SUPPORTED = 256; var JD_DEVICE_LOST_DELAY = 1500; var JD_DEVICE_DISCONNECTED_DELAY = 3e3; var RESET_IN_TIME_US = 2e6; var MAX_SERVICES_LENGTH = 59; var NEW_LISTENER = "newListener"; var REMOVE_LISTENER = "removeListener"; var CONNECTION_STATE = "connectionState"; var CONNECT = "connect"; var LOST = "lost"; var FOUND = "found"; var CONNECTING = "connecting"; var DISCONNECT = "disconnect"; var DISCONNECTING = "disconnecting"; var ANNOUNCE = "announce"; var START = "start"; var RESTART = "restart"; var STOP = "stop"; var CHANGE = "change"; var EVENT = "event"; var RENDER = "render"; var REFRESH = "refresh"; var MESSAGE = "message"; var LOG = "log"; var FIRMWARE_BLOBS_CHANGE = "firmwareBlobsChange"; var LATE = "late"; var GET_ATTEMPT = "getAttempt"; var SERVICE_CLIENT_ADDED = `serviceClientAdded`; var SERVICE_CLIENT_REMOVED = `serviceClientRemoved`; var READING_SENT = "readingSent"; var ROLE_CHANGE = "roleChange"; var ROLE_MANAGER_CHANGE = "roleManagerChange"; var SERVICE_PROVIDER_ADDED = `serviceProviderAdded`; var SERVICE_PROVIDER_REMOVED = `serviceProviderRemoved`; var IDENTIFY = "identify"; var IDENTIFY_DURATION = 2e3; var RESET = "reset"; var DATA = "data"; var SIDE_DATA = "sideData"; var CLOSE = "close"; var DISPOSE = "dispose"; var DEVICE_CONNECT = "deviceConnect"; var DEVICE_LOST = "deviceLost"; var DEVICE_FOUND = "deviceFound"; var DEVICE_DISCONNECT = "deviceDisconnect"; var DEVICE_ANNOUNCE = "deviceAnnounce"; var DEVICE_PACKET_ANNOUNCE = "devicePacketAnnounce"; var DEVICE_RESTART = "deviceRestart"; var DEVICE_CHANGE = "deviceChange"; var DEVICE_FIRMWARE_INFO = "firmwareInfo"; var DEVICE_CLEAN = "deviceClean"; var SELF_ANNOUNCE = "selfAnnounce"; var PACKET_SEND = "packetSend"; var FRAME_SEND_DISCONNECT = "frameSendDisconnect"; var PACKET_PRE_PROCESS = "packetPreProcess"; var PACKET_PROCESS = "packetProcess"; var PACKET_RECEIVE = "packetReceive"; var PACKET_RECEIVE_ANNOUNCE = "packetReceiveAnnounce"; var PACKET_RECEIVE_NO_DEVICE = "packetReceiveNoDevice"; var PACKET_EVENT = "packetEvent"; var PACKET_REPORT = "packetReport"; var PACKET_ANNOUNCE = "packetAnnounce"; var PACKET_INVALID_CRC = "packetInvalidCrc"; var PACKET_INVALID_DATA = "packetInvalidData"; var PACKET_DATA_NORMALIZE = "packetDataNormalize"; var FRAME_PROCESS = "frameProcess"; var FRAME_PROCESS_LARGE = "frameProcessLarge"; var FRAME_SEND = "frameSend"; var REPORT_RECEIVE = "reportReceive"; var REPORT_UPDATE = "reportUpdate"; var COMMAND_RECEIVE = "commandReceive"; var ERROR = "error"; var TRANSPORT_ERROR = "transportError"; var PANIC = "panic"; var TRACE = "trace"; var TIMEOUT = "timeout"; var TIMEOUT_DISCONNECT = "timeoutDisconnect"; var GLOBALS_UPDATED = "globalsUpdated"; var STATE_CHANGE = "stateChange"; var PROGRESS = "progress"; var PACKET_KIND_RW = "rw"; var PACKET_KIND_RO = "ro"; var PACKET_KIND_EVENT = "event"; var PACKET_KIND_ANNOUNCE = "announce"; var REGISTER_NODE_NAME = "register"; var REPORT_NODE_NAME = "report"; var CONST_NODE_NAME = "const"; var EVENT_NODE_NAME = "event"; var SERVICE_NODE_NAME = "service"; var SERVICE_MIXIN_NODE_NAME = "serviceMixin"; var DEVICE_NODE_NAME = "device"; var VIRTUAL_DEVICE_NODE_NAME = "virtualdevice"; var BUS_NODE_NAME = "bus"; var COMMAND_NODE_NAME = "command"; var FIELD_NODE_NAME = "field"; var PIPE_NODE_NAME = "pipe"; var PIPE_REPORT_NODE_NAME = "pipe_report"; var CRC_ACK_NODE_NAME = "crcAck"; var SERVICE_TEST_NODE_NAME = "serviceTest"; var REGISTER_REFRESH_TIMEOUT = 150; var REGISTER_REFRESH_RETRY_0 = 30; var REGISTER_REFRESH_RETRY_1 = 80; var REGISTER_POLL_STREAMING_INTERVAL = 5e3; var REGISTER_POLL_FIRST_REPORT_INTERVAL = 400; var REGISTER_POLL_REPORT_INTERVAL = 5001; var REGISTER_POLL_REPORT_MAX_INTERVAL = 6e4; var REGISTER_POLL_REPORT_VOLATILE_INTERVAL = 1e3; var REGISTER_POLL_REPORT_VOLATILE_MAX_INTERVAL = 5e3; var REGISTER_OPTIONAL_POLL_COUNT = 3; var STREAMING_DEFAULT_INTERVAL = 50; var FLASH_MAX_DEVICES = 6; var PING_LOGGERS_POLL = 2400; var ROLE_MANAGER_POLL = 1500; var REFRESH_REGISTER_POLL = 50; var USB_TRANSPORT = "usb"; var BLUETOOTH_TRANSPORT = "bluetooth"; var SERIAL_TRANSPORT = "serial"; var PACKETIO_TRANSPORT = "packetio"; var WEBSOCKET_TRANSPORT = "web"; var NODESOCKET_TRANSPORT = "tcp"; var HF2_TIMEOUT = 1e3; var META_ACK = "ACK"; var META_ACK_FAILED = "ACK_FAILED"; var META_PIPE = "PIPE"; var META_GET = "GET"; var META_TRACE = "TRACE"; var META_TRACE_DESCRIPTION = "TRACE_DESCRIPTION"; var META_NOT_IMPLEMENTED = "NOT_IMPLEMENTED"; var REGISTER_PRE_GET = "registerPreGet"; var TRACE_FILTER_HORIZON = 100; var EMBED_MIN_ASPECT_RATIO = 1.22; var BLUETOOTH_JACDAC_SERVICE = "f8530001-a97f-49f5-a554-3e373fbea2d5"; var BLUETOOTH_JACDAC_RX_CHARACTERISTIC = "f8530002-a97f-49f5-a554-3e373fbea2d5"; var BLUETOOTH_JACDAC_TX_CHARACTERISTIC = "f8530003-a97f-49f5-a554-3e373fbea2d5"; var BLUETOOTH_JACDAC_DIAG_CHARACTERISTIC = "f8530004-a97f-49f5-a554-3e373fbea2d5"; var TRANSPORT_CONNECT_RETRY_DELAY = 500; var ERROR_TRANSPORT_DEVICE_LOCKED = "transport/device-locked"; var ROLE_BOUND = "roleBound"; var ROLE_UNBOUND = "roleUnbound"; var ROLE_HAS_NO_SERVICE = "roleHasNoService"; var BOUND = "bound"; var UNBOUND = "unbound"; var JACDAC_ERROR = "JacdacError"; var ERROR_TIMEOUT = "timeout"; var ERROR_TRANSPORT_CLOSED = "transport/closed"; var ERROR_MICROBIT_V1 = "microbit/v1-not-supported"; var ERROR_MICROBIT_UNKNOWN = "microbit/unknown-hardware-revision"; var ERROR_MICROBIT_JACDAC_MISSING = "microbit/jacdac-missing"; var ERROR_MICROBIT_INVALID_MEMORY = "microbit/invalid-memory"; var ERROR_TRANSPORT_HF2_NOT_SUPPORTED = "transport/hf2-not-supported"; var ERROR_TRANSPORT_WEBSOCKET_NOT_SUPPORTED = "transport/websocket-not-supported"; var ERROR_NO_ACK = "no-ack"; var ROLE_QUERY_SERVICE_OFFSET = "srvo"; var ROLE_QUERY_SERVICE_INDEX = "srvi"; var ROLE_QUERY_DEVICE = "dev"; var ROLE_QUERY_SELF_DEVICE = "self"; var DOCS_ROOT = "https://microsoft.github.io/jacdac-docs/"; // jacdac-spec/spectool/jdutils.ts function isMixinService(serviceClass2) { return (serviceClass2 & 536870912) === 536870912; } function parseIntFloat(spec, w, allowFloat = false) { if (/^-?0x[a-f\d_]+$/i.test(w) || /^-?[\d_]+$/.test(w)) { const v = parseInt(w.replace(/_/g, "")); if (isNaN(v)) throw new Error("can't parse int: " + w); return v; } if (allowFloat && /^-?\d*(\.\d*)?(e(-?)\d+)?$/.test(w)) { const v = parseFloat(w); if (isNaN(v)) throw new Error("can't parse float: " + w); return v; } const ww = w.split(/\./); if (ww.length != 2) { throw new Error(`expecting int or enum member here`); } const en = spec.enums[ww[0]]; if (!en) { throw new Error(`${ww[0]} is not an enum type`); } if (!en.members.hasOwnProperty(ww[1])) throw new Error(`${ww[1]} is not a member of ${ww[0]}`); return en.members[ww[1]] || 0; } // jacdac-spec/spectool/jdspec.ts var DEVICE_IMAGE_WIDTH = 1024; var DEVICE_IMAGE_HEIGHT = 768; var unitDescription = { "\xB0": "angle", "\xB0/s": "rotation rate", "\xB0/s2": "rotation acceleration", m: "meter", kg: "kilogram", s: "second", A: "ampere", K: "kelvin", cd: "candela", mol: "mole", Hz: "hertz", rad: "radian", sr: "steradian", N: "newton", Pa: "pascal", J: "joule", W: "watt", C: "coulomb", V: "volt", F: "farad", Ohm: "ohm", S: "siemens", Wb: "weber", T: "tesla", H: "henry", "\xB0C": "degrees Celsius", lm: "lumen", lx: "lux", Bq: "becquerel", Gy: "gray", Sv: "sievert", kat: "katal", m2: "square meter (area)", m3: "cubic meter (volume)", "m/s": "meter per second (velocity)", "m/s2": "meter per square second (acceleration)", "m3/s": "cubic meter per second (flow rate)", "W/m2": "watt per square meter (irradiance)", "cd/m2": "candela per square meter (luminance)", bit: "bit (information content)", "bit/s": "bit per second (data rate)", baud: "symbols per second", lat: "degrees latitude", lon: "degrees longitude", pH: "pH value (acidity; logarithmic quantity)", dB: "decibel (logarithmic quantity)", dBW: "decibel relative to 1 W (power level)", count: "1 (counter value)", "/": "1 (ratio e.g., value of a switch)", "%RH": "Percentage (Relative Humidity)", "%EL": "Percentage (remaining battery energy level)", EL: "seconds (remaining battery energy level)", "1/s": "1 per second (event rate)", "S/m": "Siemens per meter (conductivity)", B: "Byte (information content)", VA: "volt-ampere (Apparent Power)", VAs: "volt-ampere second (Apparent Energy)", var: "volt-ampere reactive (Reactive Power)", vars: "volt-ampere-reactive second (Reactive Energy)", "J/m": "joule per meter (Energy per distance)", "kg/m3": "kilogram per cubic meter (mass density, mass concentration)", "s/60\xB0": "servo speed (time to travel 60\xB0)", "kg/cm": "torque", hsv: "bit HSV color", rgb: "RGB color", rpm: "revolutions per minute", uv: "UV index", lux: "illuminance", bpm: "beats per minute", mcd: "micro candella", px: "pixel", AQI: "air quality index" }; var secondaryUnitConverters = { ms: { name: "millisecond", unit: "s", scale: 1 / 1e3, offset: 0 }, min: { name: "minute", unit: "s", scale: 60, offset: 0 }, h: { name: "hour", unit: "s", scale: 3600, offset: 0 }, MHz: { name: "megahertz", unit: "Hz", scale: 1e6, offset: 0 }, kW: { name: "kilowatt", unit: "W", scale: 1e3, offset: 0 }, kVA: { name: "kilovolt-ampere", unit: "VA", scale: 1e3, offset: 0 }, kvar: { name: "kilovar", unit: "var", scale: 1e3, offset: 0 }, Ah: { name: "ampere-hour", unit: "C", scale: 3600, offset: 0 }, Wh: { name: "watt-hour", unit: "J", scale: 3600, offset: 0 }, kWh: { name: "kilowatt-hour", unit: "J", scale: 36e5, offset: 0 }, varh: { name: "var-hour", unit: "vars", scale: 3600, offset: 0 }, kvarh: { name: "kilovar-hour", unit: "vars", scale: 36e5, offset: 0 }, kVAh: { name: "kilovolt-ampere-hour", unit: "VAs", scale: 36e5, offset: 0 }, "Wh/km": { name: "watt-hour per kilometer", unit: "J/m", scale: 3.6, offset: 0 }, KB: { name: "kilobyte", unit: "B", scale: 1e3, offset: 0 }, KiB: { name: "kibibyte", unit: "B", scale: 1024, offset: 0 }, GB: { name: "gigabyte", unit: "B", scale: 1e9, offset: 0 }, "Mbit/s": { name: "megabit per second", unit: "bit/s", scale: 1e6, offset: 0 }, "B/s": { name: "byte per second", unit: "bit/s", scale: 8, offset: 0 }, "MB/s": { name: "megabyte per second", unit: "bit/s", scale: 8e6, offset: 0 }, mV: { name: "millivolt", unit: "V", scale: 1 / 1e3, offset: 0 }, mA: { name: "milliampere", unit: "A", scale: 1 / 1e3, offset: 0 }, dBm: { name: "decibel (milliwatt)", unit: "dBW", scale: 1, offset: -30 }, "ug/m3": { name: "microgram per cubic meter", unit: "kg/m3", scale: 1e-9, offset: 0 }, "mm/h": { name: "millimeter per hour", unit: "m/s", scale: 1 / 36e5, offset: 0 }, "m/h": { name: "meter per hour", unit: "m/s", scale: 1 / 3600, offset: 0 }, "cm/s": { name: "centimeter per seconds", unit: "m/s", scale: 1 / 100, offset: 0 }, ppm: { name: "parts per million", unit: "/", scale: 1e-6, offset: 0 }, ppb: { name: "parts per billion", unit: "/", scale: 1e-9, offset: 0 }, "/100": { name: "percent", unit: "/", scale: 1 / 100, offset: 0 }, "%": { name: "percent", unit: "/", scale: 1 / 100, offset: 0 }, "/1000": { name: "permille", unit: "/", scale: 1 / 1e3, offset: 0 }, hPa: { name: "hectopascal", unit: "Pa", scale: 100, offset: 0 }, mm: { name: "millimeter", unit: "m", scale: 1 / 1e3, offset: 0 }, cm: { name: "centimeter", unit: "m", scale: 1 / 100, offset: 0 }, km: { name: "kilometer", unit: "m", scale: 1e3, offset: 0 }, "km/h": { name: "kilometer per hour", unit: "m/s", scale: 1 / 3.6, offset: 0 }, "8ms": { name: "8 milliseconds", unit: "s", scale: 8 / 1e3, offset: 0 }, nm: { name: "nanometer", unit: "m", scale: 1e-9, offset: 0 }, nT: { name: "nano Tesla", unit: "T", scale: 1e-9, offset: 0 }, // compat with previous Jacdac versions frac: { name: "ratio", unit: "/", scale: 1, offset: 0 }, us: { name: "micro seconds", unit: "s", scale: 1e-6, offset: 0 }, mWh: { name: "micro watt-hour", unit: "J", scale: 36e-4, offset: 0 }, g: { name: "earth gravity", unit: "m/s2", scale: 9.80665, offset: 0 }, "#": { name: "count", unit: "#", scale: 1, offset: 0 }, AudHz: { name: "Audible Frequency", unit: "Hz", scale: 1, offset: 0 } }; var encodings = { json: "JSON", bitset: "bitset" }; function resolveUnit(unit) { if (!unit) return { name: "", scale: 1, offset: 1 }; const su = secondaryUnitConverters[unit]; if (su) return su; const name = unitDescription[unit]; if (name) return { name, unit, scale: 1, offset: 0 }; return void 0; } function units() { const r = []; Object.keys(unitDescription).forEach((k) => { r.push({ name: k, description: unitDescription[k] }); Object.keys(secondaryUnitConverters).filter((scd) => secondaryUnitConverters[scd].unit === k).forEach( (scd) => r.push({ name: scd, description: secondaryUnitConverters[scd].name }) ); }); r.sort((l, r2) => l.name.localeCompare(r2.name)); return r; } var identifierRanges = { rw: [ [1, 127], [128, 255], [512, 3839], // custom [3840, 4095] // impl ], ro: [ [256, 383], [384, 511], [512, 3839], // custom [3840, 4095] // impl ], const: [ [256, 383], [384, 511], [512, 3839], // custom [3840, 4095] // impl ], command: [ [0, 127], [128, 3839], [3840, 4095] ], report: [ [0, 127], [128, 3839], [3840, 4095] ], event: [ [0, 127], // system [128, 255] ] }; function parseServiceSpecificationMarkdownToJSON(filecontent, includes, filename = "") { filecontent = (filecontent || "").replace(/\r/g, ""); const info = { name: "", status: "experimental", shortId: filename.replace(/\.md$/, "").replace(/.*\//, ""), camelName: "", shortName: "", extends: [], notes: {}, classIdentifier: 0, enums: {}, constants: {}, packets: [], tags: [] }; let backticksType = null; let enumInfo = null; let packetInfo = null; let pipePacket = null; let errors = []; let lineNo = 0; let noteId = "short"; let lastCmd; let packetsToDescribe; let nextModifier = ""; const systemInfo = includes?.["_system"]; const usedIds = {}; for (const prev of values(includes || {})) { if (prev.catalog && prev.classIdentifier) usedIds[prev.classIdentifier + ""] = prev.name; } try { if (includes["_system"] && includes["_base"]) processInclude("_base"); for (const line of filecontent.split(/\n/)) { lineNo++; processLine(line); } } catch (e) { error("exception: " + e.message); } if (errors.length) info.errors = errors; for (const k of Object.keys(info.notes)) info.notes[k] = normalizeMD(info.notes[k]); for (const v of info.packets) v.description = normalizeMD(v.description); if (!info.camelName) info.camelName = camelize( info.name.replace(/\s+/g, " ").replace(/[ -](.)/g, (f, l) => l.toUpperCase()).replace(/[^\w]+/g, "_") ); if (!info.shortName) info.shortName = info.camelName; if (info.camelName == "system") info.classIdentifier = 536870897; else if (info.camelName == "base") info.classIdentifier = 536870899; else if (info.camelName == "sensor") info.classIdentifier = 536870898; if (info.shortName != "control" && !info.classIdentifier) error("identifier: not specified"); info.packets.forEach((pkt) => pkt.packFormat = packFormat(info, pkt)); return info; function processLine(line) { if (backticksType) { if (line.trim() == "```") { const prev = backticksType; backticksType = null; if (prev == "default") return; } } else { const m = /^```(.*)/.exec(line); if (m) { backticksType = m[1] || "default"; if (backticksType == "default") return; } } const interpret = backticksType == "default" || backticksType == null && line.slice(0, 4) == " "; if (!interpret) { const m = /^(#+)\s*(.*)/.exec(line); if (m) { const [, hd, cont] = m; packetsToDescribe = null; const newNoteId = cont.trim().toLowerCase(); if (hd == "#" && !info.name) { info.name = cont.trim(); line = ""; } else if (newNoteId == "registers" || newNoteId == "commands" || newNoteId == "events" || newNoteId == "examples") { noteId = newNoteId; line = ""; } else { if (noteId == "short") noteId = "long"; } } if (packetsToDescribe) { for (const iface of packetsToDescribe) iface.description += line + "\n"; } else { if (line || info.notes[noteId]) { if (!info.notes[noteId]) info.notes[noteId] = ""; info.notes[noteId] += line + "\n"; } } } else { if (packetsToDescribe && packetsToDescribe[0].description) packetsToDescribe = null; const expanded = line.replace(/\/\/.*/, "").replace(/[?@:=,{};]/g, (s) => " " + s + " ").trim(); if (!expanded) return; const words = expanded.split(/\s+/); if (/^[;,]/.test(words[words.length - 1])) words.pop(); let cmd = words[0]; if (words[1] == ":" || words[1] == "=") cmd = ":"; switch (cmd) { case "type": case "enum": case "flags": startEnum(words); break; case "define": constant(words); break; case "meta": case "pipe": case "report": case "command": case "const": case "ro": case "rw": case "event": case "client": case "volatile": case "lowlevel": case "unique": case "restricted": case "packed": startPacket(words); break; case "}": if (packetInfo) { finishPacket(); } else if (enumInfo) { enumInfo = null; } else { error("nothing to end here"); } break; default: if (packetInfo) packetField(words); else if (enumInfo) enumMember(words); else metadataMember(words); } } } function finishPacket() { const paderr = paddingError(packetInfo); if (paderr) { if (!packetInfo.packed) error( `${paderr} in ${packetInfo.kind} ${packetInfo.name}; you can also add 'packed' attribute` ); } else { if (packetInfo.packed) error( `${packetInfo.kind} ${packetInfo.name} has unnecessary 'packed' attribute` ); } let repeats = false; let hadzero = false; for (const p of packetInfo.fields) { if (hadzero) { error( `field ${p.name} in ${packetInfo.kind} ${packetInfo.name} follows a variable-sized field` ); break; } if (p.startRepeats) { if (repeats) error( `repeats: can only be specified once; in ${packetInfo.kind} ${packetInfo.name}` ); repeats = true; } if (p.storage == 0 && p.type != "string0") { if (repeats) { error( `variable-sized field ${p.name} in ${packetInfo.kind} ${packetInfo.name} cannot repeat` ); break; } hadzero = true; } } const pid = packetInfo.identifier; const ranges = identifierRanges[packetInfo.kind]; if (packetInfo.name != "set_register" && packetInfo.name != "get_register" && ranges && !ranges.some((range2) => range2[0] <= pid && pid <= range2[1])) error( `${packetInfo.name} identifier ${toHex( pid )} out of range, expected in ${ranges.map((range2) => `[${range2.map(toHex).join(", ")}]`).join(", ")}` ); if ([ "reading_error", "min_reading", "max_reading", "reading_resolution" ].indexOf(packetInfo.identifierName) > -1) { const regid = packetInfo.identifierName; if (packetInfo.fields.length > 1) error(`${regid} must be a number`); const reading = info.packets.find( (pkt) => pkt.kind === "ro" && pkt.identifierName === "reading" ); if (!reading) error(`${regid} register without a reading register`); else if (packetInfo.fields[0].unit !== reading.fields[0].unit) error( `${regid} unit (${packetInfo.fields[0].unit}) and reading unit (${reading.fields[0].unit}) must match` ); } packetInfo = null; } function normalizeMD(md) { return md.replace(/^\s+/, "").replace(/\s+$/, ""); } function checkBraces(words) { if (enumInfo || packetInfo) error("already in braces"); if (words) { if (words[2] != "{") error(`expecting: ${words[0]} NAME {`); } enumInfo = null; packetInfo = null; } function forceSection(sectionId) { if (noteId != sectionId) { error(`this is only allowed in ## ${sectionId} not in ## ${noteId}`); } } function generalKind(k) { switch (k) { case "const": case "ro": case "rw": return "rw"; default: return k; } } function startPacket(words) { checkBraces(null); let client = void 0; let lowLevel = void 0; let restricted = void 0; let unique2 = void 0; let internal = void 0; let volatile = void 0; let packed2 = void 0; function processAttributes() { while (words.length) { if (words[0] === "restricted") { restricted = true; } else if (words[0] === "client") { client = true; } else if (words[0] === "lowlevel") { lowLevel = true; } else if (words[0] === "unique") { unique2 = true; } else if (words[0] === "internal") { internal = true; } else if (words[0] === "volatile") { volatile = true; } else if (words[0] === "packed") { packed2 = true; } else { break; } words.shift(); } } processAttributes(); const kindSt = words.shift(); let kind = "command"; if (kindSt == "meta") { forceSection("commands"); let w2 = words.shift(); if (w2 == "pipe") w2 = words.shift(); if (w2 == "report" || w2 == "command") kind = "meta_pipe_" + w2; else error("invalid token after meta"); } else if (kindSt == "pipe") { forceSection("commands"); const w2 = words.shift(); if (w2 == "report" || w2 == "command") kind = "pipe_" + w2; else error("invalid token after pipe"); } else { kind = kindSt; } processAttributes(); if (unique2 && kind !== "command") error("unique only applies to commands"); if (volatile && kind != "ro" && kind != "rw") error("volatile can only modify ro or rw"); let name = words.shift(); const isReport = kind == "report"; if (isReport && lastCmd && !/^\w+$/.test(name)) { words.unshift(name); name = lastCmd.name; } packetInfo = { kind, name: normalizeName(name), identifier: void 0, description: "", fields: [], internal, client, lowLevel, unique: unique2, volatile, restricted, packed: packed2 }; if (isReport && lastCmd && name == lastCmd.name) { packetInfo.secondary = true; lastCmd.hasReport = true; } if (!packetsToDescribe) packetsToDescribe = []; packetsToDescribe.push(packetInfo); if (words[0] == "?") { words.shift(); packetInfo.optional = true; } const prev = info.packets.filter((p) => p.name == packetInfo.name); if (prev.length == 0) { } else if (prev.length == 1 && prev[0].kind == "command" && packetInfo.kind == "report") { } else { error(`packet redefinition ${prev.map((p) => p.name).join(", ")} `); } if (/pipe/.test(kind)) { if (!pipePacket) error( "pipe definitions can only occur after the pipe-open packet" ); else packetInfo.pipeType = pipePacket.pipeType; } const atat = words.indexOf("@"); if (kind == "pipe_command" || kind == "pipe_report") { packetInfo.identifier = 0; } else if (atat >= 0) { const w = words[atat + 1]; let v = parseInt(w); let isSet2 = true; if (isNaN(v)) { v = 0; isSet2 = false; if (systemInfo) { const systemPacket = systemInfo.packets.find( (p) => p.name == w ); if (systemPacket) { v = systemPacket.identifier; packetInfo.identifierName = w; if (systemPacket.kind != kind) error( `kind mismatch on ${w}: ${systemPacket.kind} vs ${kind}` ); else isSet2 = true; } else error(`${w} not found in _system`); } else { error(`${w} cannot be resolved, since _system is missing`); } } if (kind === "ro" && (v === 257 || v === 262)) packetInfo.volatile = true; let isUser = false; let isSystem = false; let isHigh = 512 <= v && v <= 3839; switch (kind) { case "const": case "ro": forceSection("registers"); isSystem = 256 <= v && v <= 383; isUser = 384 <= v && v <= 511; break; case "rw": forceSection("registers"); isSystem = 0 <= v && v <= 127; isUser = 128 <= v && v <= 255; break; case "report": case "command": forceSection("commands"); isSystem = 0 <= v && v <= 127; isUser = 128 <= v && v <= 255; isHigh = 256 <= v && v <= 3839; break; case "event": forceSection("events"); isSystem = 0 <= v && v <= 127; isUser = 128 <= v && v <= 255; break; } if (isUser) { } else if (isSystem) { if (!packetInfo.identifierName) warn2( `${kind} @ ${toHex( v )} should be expressed with a name from _system.md` ); } else if (isHigh) { if (!info.highCommands) warn2( `${kind} @ ${toHex( v )} is from the extended range but 'high: 1' missing` ); } packetInfo.identifier = v; words.splice(atat, 2); } else { if (isReport && lastCmd) packetInfo.identifier = lastCmd.identifier; else error(`@ not found at ${packetInfo.name}`); } if (info.packets.some( (p) => generalKind(p.kind) == generalKind(packetInfo.kind) && (!/pipe/.test(p.kind) || p.pipeType == packetInfo.pipeType) && p.identifier == packetInfo.identifier )) { error("packet identifier already used"); } info.packets.push(packetInfo); if (kind == "command") lastCmd = packetInfo; else lastCmd = null; if (words[0] == "=" || words[0] == ":") { words.unshift("_"); packetField(words); finishPacket(); } else { const last = words.shift(); if (last == "{") { if (words[0] == "...") words.shift(); if (words[0] == "}") { words.shift(); finishPacket(); } if (words.length) error(`excessive tokens: ${words[0]}...`); } else { if (last === void 0 && kind == "event") { finishPacket(); } else { error("expecting '{'"); } } } } function rangeCheck(tp, v) { const [storage, type, typeShift] = normalizeStorageType(tp); if (isNaN(v)) return v; if (storage == 0) { error(`numeric values like ${v} not allowed for ${tp}`); return v; } if (v < 0 && storage > 0) { error(`negative values like ${v} not allowed for ${tp}`); return v; } if (Math.floor(v) != v && typeShift == 0) { error(`only integer values allowed for ${tp}; got ${v}`); return v; } let bits = storage < 0 ? -storage * 8 - 1 : storage * 8; bits -= typeShift || 0; let max = 1; while (bits--) max *= 2; if (-v == max) { } else if (max == 1 && v == 1) { } else { if (Math.abs(v) >= max) { error(`value ${v} is out of range for ${tp}`); return v; } } return v; } function parseVal(words) { const eq = words.shift(); if (eq != "=" && eq != ":") error("expecting '='"); const val = words.shift(); return parseIntCheck(val, true); } function constant(words) { if (words.length != 3) { error(`define syntax is "define name value" (${words.join(" ")}}`); return; } const name = words[1]; const svalue = words[2]; const hex = /^0x/.test(svalue); const value = hex ? parseInt(svalue, 16) : parseInt(svalue); if (isNaN(value)) { error("invalid numeric value for constant"); return; } info.constants[name] = { value, hex }; } function packetField(words) { if (words.length == 2 && (words[0] == "repeats" || words[0] == "segmented" || words[0] == "multi-segmented")) { nextModifier = words[0]; return; } const name = normalizeName(words.shift()); let defaultValue = void 0; let isOptional = void 0; let op = words.shift(); if (op == "?") { isOptional = true; op = words.shift(); } if (op == "=") { defaultValue = parseIntCheck(words.shift(), true); op = words.shift(); } if (op != ":") error("expecting ':'"); const tp = words.shift(); const [storage, type, typeShift] = normalizeStorageType(tp); const isFloat = typeShift === null || void 0; let tok = words.shift(); let unit; let encoding; if (tok != "{") { if (type === "string" || type === "bytes") encoding = normalizeEncoding(tok); else unit = normalizeUnit(tok); tok = words.shift(); } if (defaultValue !== void 0) rangeCheck(tp, defaultValue); let shift = typeShift || void 0; if (unit == "/") { if (!/^(u0|i1)\.\d+$/.test(tp)) error( `fraction unit must be used with u0.yyy or i1.yyy data types (got ${tp})` ); shift = Math.abs(storage) * 8; if (storage < 0) shift -= 1; } const field = { name, unit, encoding, shift, isFloat, type, storage, isSimpleType: canonicalType(storage) == type || void 0, defaultValue, isOptional, multiSegmented: nextModifier == "multi-segmented" || void 0, segmented: nextModifier == "segmented" || nextModifier == "multi-segmented" || void 0, startRepeats: nextModifier == "repeats" || void 0 }; if (!unit) delete field.unit; if (!encoding) delete field.encoding; if (tok == "{") { while (words.length) { tok = words.shift(); if (tok == "}") break; tok = camelize(tok); switch (tok) { case "maxBytes": { ; field[tok] = rangeCheck("u8", parseVal(words)); break; } case "typicalMin": case "typicalMax": case "absoluteMin": case "absoluteMax": { ; field[tok] = rangeCheck(tp, parseVal(words)); break; } case "preferredInterval": { if (packetInfo[tok] !== void 0) error(`field ${tok} already set`); packetInfo[tok] = rangeCheck( "u32", parseVal(words) ); break; } default: error("unknown constraint: " + tok); break; } if (words[0] == ",") words.shift(); } if (tok == "}") tok = null; } if (tok) error(`excessive tokens at the end of member: ${tok}...`); if (field.typicalMin === void 0 && field.typicalMax !== void 0 && storage > 0) field.typicalMin = 0; if (field.absoluteMin === void 0 && field.absoluteMax !== void 0 && storage > 0) field.absoluteMin = 0; if (!field.storage && field.maxBytes) field.storage = field.maxBytes; if (/pipe/.test(type)) { packetInfo.pipeType = packetInfo.name; if (pipePacket && pipePacket.name == packetInfo.name && packetInfo.kind == "report") { } else { pipePacket = packetInfo; } } if (!field.isOptional && packetInfo.fields[packetInfo.fields.length - 1]?.isOptional) { error(`all fields after an optional field have to optional`); } packetInfo.fields.push(field); nextModifier = void 0; } function startEnum(words) { checkBraces(null); let isClosed = false; if (words[0] == "type") { if (words[2] != ":") error("expecting: type NAME : TYPE"); if (!words[4]) isClosed = true; else if (words[4] == "{") { if (words[5] == "}") isClosed = true; else if (words[5]) error("excessive syntax"); } else { error("expecting {"); } } else { if (words[2] != ":" || words[4] != "{") error("expecting: enum NAME : TYPE {"); } enumInfo = { name: normalizeName(words[1]), storage: normalizeStorageType(words[3])[0], isFlags: words[0] == "flags" || void 0, isExtensible: words[0] == "type" || void 0, members: {} }; if (info.enums[enumInfo.name]) error("enum redefinition"); info.enums[enumInfo.name] = enumInfo; if (isClosed) enumInfo = null; } function enumMember(words) { if (words[1] != "=" || words.length != 3) error(`expecting: FIELD_NAME = INTEGER`); enumInfo.members[normalizeName(words[0])] = rangeCheck( canonicalType(enumInfo.storage), parseIntCheck(words[2]) ); } function parseIntCheck(w, allowFloat = false) { try { return parseIntFloat(info, w, allowFloat); } catch (e) { error(e.message); return 0; } } function looksRandom2(n) { const s = n.toString(16); const h = "0123456789abcdef"; for (let i = 0; i < h.length; ++i) { const hh = h[i]; if (s.indexOf(hh + hh + hh) >= 0) return false; } if (/f00d|dead|deaf|beef/.test(s)) return false; return true; } function genRandom() { for (; ; ) { const m = Math.random() * 268435455 | 268435456; if (looksRandom2(m)) return m; } } function metadataMember(words) { if (!((words[1] == "=" || words[1] == ":") && (words[0] == "tags" || words.length == 3))) error(`expecting: FIELD_NAME = VALUE or FIELD_NAME : VALUE`); switch (words[0]) { case "extends": processInclude(words[2]); break; case "class": case "identifier": { info.classIdentifier = parseIntCheck(words[2]); if (info.name != "Control" && info.classIdentifier == 0) info.classIdentifier = 1; const gen = `how about ${toHex(genRandom())}`; if (!(info.classIdentifier == 0 || 268435457 <= info.classIdentifier && info.classIdentifier <= 536870656 || 536870913 <= info.classIdentifier && info.classIdentifier <= 12884901632)) error(`class identifier out of range; ${gen}`); if (!looksRandom2(info.classIdentifier)) error(`class identifier doesn't look random; ${gen}`); if (usedIds[info.classIdentifier + ""]) error( `class identifier ${toHex( info.classIdentifier )} already used in ${usedIds[info.classIdentifier + ""]}; ${gen}` ); break; } case "camel": info.camelName = words[2]; break; case "short": info.shortName = words[2]; break; case "high": info.highCommands = !!parseIntCheck(words[2]); break; case "status": if (["stable", "experimental", "deprecated", "rc"].indexOf( words[2] ) > -1) info.status = words[2]; else error("unknown status"); break; case "group": info.group = capitalize(words.slice(2).join(" ")); break; case "restricted": if (words[2] == "true") info.restricted = true; else if (words[2] == "false") delete info.restricted; else error("restricted: should be true or false"); break; case "tags": { const tags = words.slice(2).filter((w) => w != "," && w != ";"); info.tags = info.tags.concat(tags); break; } default: error("unknown metadata field: " + words[0]); break; } } function processInclude(name) { if (name == "_system") return; const inner = includes[name]; if (!inner) return error("include file not found: " + name); if (info.packets.some((pkt) => !pkt.derived) || values(info.enums).some((e) => !e.derived)) error("extends: only allowed on top of the .md file"); if (inner.errors) errors = errors.concat(inner.errors); const innerEnums = clone2(inner.enums); Object.keys(innerEnums).filter((k) => !info.enums[k]).forEach((k) => { const ie = innerEnums[k]; ie.derived = name; info.enums[k] = ie; }); const innerPackets = clone2( inner.packets.filter( (pkt) => !info.packets.find( (ipkt) => ipkt.kind === pkt.kind && ipkt.identifier === pkt.identifier ) ) ); innerPackets.forEach((pkt) => pkt.derived = name); info.packets = [...info.packets, ...innerPackets]; if (inner.highCommands) info.highCommands = true; info.extends = inner.extends.concat([name]); } function clone2(v) { return JSON.parse(JSON.stringify(v)); } function error(msg) { if (!msg) msg = "syntax error"; if (errors.some((e) => e.line == lineNo && e.message == msg)) return; errors.push({ file: filename, line: lineNo, message: msg }); } function warn2(msg) { if (info.camelName == "system") return; if (errors.some((e) => e.line == lineNo && e.message == msg)) return; errors.push({ file: filename, line: lineNo, message: msg }); } function normalizeName(n) { if (!/^\w+$/.test(n)) error("expecting name here"); if (n.length > 31) error(`name '${n}' too long`); return n; } function normalizeStorageType(tp) { if (info.enums[tp]) return [info.enums[tp].storage, tp, 0]; if (!tp) error("expecting type here"); const tp2 = tp.replace(/_t$/, "").toLowerCase(); const m = /^([ui])(\d+)\.(\d+)$/.exec(tp2); if (m) { const a = parseIntCheck(m[2]); const b = parseIntCheck(m[3]); const len = a + b; if (!(len == 8 || len == 16 || len == 32 || len == 64)) error(`fixed point ${tp} can't be ${len} bits`); if (a == 0 && m[1] == "i") error( `fixed point ${tp} can't be i0.X; has to be at least i1.X` ); return [(m[1] == "i" ? -1 : 1) * (len >> 3), tp2, b]; } switch (tp2) { case "bool": return [1, tp2, 0]; case "i8": case "u8": case "i16": case "u16": case "i32": case "u32": case "i64": case "u64": { let sz = parseIntCheck(tp2.replace(/^./, "")) >> 3; if (tp2[0] == "i") sz = -sz; return [sz, tp2, 0]; } case "f16": return [2, tp2, null]; case "f32": return [4, tp2, null]; case "f64": return [8, tp2, null]; case "pipe": return [12, tp2, 0]; case "pipe_port": return [2, tp2, 0]; case "devid": return [8, tp2, 0]; case "bytes": case "string": case "string0": return [0, tp2, 0]; default: { const m2 = /^u8\[(\d+)\]$/.exec(tp2); if (m2) return [parseIntCheck(m2[1]), tp2, 0]; error("unknown type: " + tp + " " + tp2); return [4, tp2, 0]; } } } function normalizeEncoding(unit) { return unit && encodings[unit.toLowerCase()] || void 0; } function normalizeUnit(unit) { if (unit === void 0 || unit === null) return void 0; if (unitDescription[unit] || secondaryUnitConverters[unit]) return unit; error(`expecting unit, got '${unit}'`); return void 0; } function paddingError(iface) { let byteOffset = 0; for (const m of iface.fields) { const sz = memberSize(m); if (sz == 0) break; const pad = m.type == "bytes" ? 1 : sz > 8 ? 8 : sz; if (!/^u8\[/.test(m.type) && byteOffset % pad != 0) return `need padding of ${pad - byteOffset % pad} byte(s) before ${m.name}`; byteOffset += sz; } return null; } } function values(o) { const r = []; for (const k of Object.keys(o)) r.push(o[k]); return r; } function toUpper(name) { return name?.replace(/([a-z])([A-Z])/g, (x, a, b) => a + "_" + b).toUpperCase(); } function toLower(name) { return name?.replace(/([a-z])([A-Z])/g, (x, a, b) => a + "_" + b).toLowerCase(); } function packed(iface) { if (!iface.packed) return ""; else return " __attribute__((packed))"; } function cStorage(tp) { if (tp == 0 || [1, 2, 4, 8].indexOf(Math.abs(tp)) < 0) return "bytes"; if (tp < 0) return `int${-tp * 8}_t`; else return `uint${tp * 8}_t`; } function cSharpStorage(tp) { if (tp == 0 || [1, 2, 4, 8].indexOf(Math.abs(tp)) < 0) return "bytes"; switch (tp) { case -1: return "sbyte"; case 1: return "byte"; case -2: return "short"; case 2: return "ushort"; case -4: return "int"; case 4: return "uint"; } return `unknown({${tp})`; } function canonicalType(tp) { if (tp == 0) return "bytes"; if (tp < 0) return `i${-tp * 8}`; else return `u${tp * 8}`; } function isRegister(k) { return k == "ro" || k == "rw" || k == "const"; } function toHex(n) { if (n === void 0) return ""; if (n < 0) return "-" + toHex(n); return "0x" + n.toString(16); } function unitPref(f) { if (!f.unit) return ""; else return prettyUnit(f.unit) + " "; } function prettyUnit(u) { switch (u) { case "us": return "\u03BCs"; case "C": return "\xB0C"; case "/": return "ratio"; default: return u; } } function toPython(info, language) { const r = [`# Autogenerated constants for ${info.name} service`]; if (Object.keys(info.enums).length) r.push("from enum import IntEnum"); const docsLength = r.length; const desktop = language === "py"; const packFormats = {}; if (desktop) r.push("from jacdac.constants import *"); let pref = "JD_" + toUpper(info.shortName) + "_"; if (info.shortId[0] == "_") pref = "JD_"; if (info.shortId[0] != "_") r.push( `JD_SERVICE_CLASS_${toUpper(info.shortName)} = const(${toHex( info.classIdentifier )})` ); for (const cst in info.constants) { const { value, hex } = info.constants[cst]; r.push( `JD_${toUpper(cst)} = const(${hex ? value.toString() : toHex(value)}) ` ); } if (Object.keys(info.enums).length) { for (const en of values(info.enums).filter((en2) => !en2.derived)) { r.push(""); r.push(""); r.push( `class ${upperCamel(info.shortName)}${upperCamel( en.name )}(IntEnum):` ); for (const k of Object.keys(en.members)) r.push(` ${toUpper(k)} = const(${toHex(en.members[k])})`); } r.push(""); r.push(""); } let useIdentifiers = false; for (const pkt of info.packets) { if (pkt.derived) continue; if (!pkt.secondary && pkt.kind != "pipe_command" && pkt.kind != "pipe_report") { let inner = "CMD"; if (isRegister(pkt.kind)) inner = "REG"; else if (pkt.kind == "event") inner = "EV"; else if (pkt.kind == "meta_pipe_command" || pkt.kind == "meta_pipe_report") inner = "PIPE"; let val = toHex(pkt.identifier); if (pkt.identifierName) { val = "JD_" + inner + "_" + toUpper(pkt.identifierName); useIdentifiers = true; } const name = pref + inner + "_" + toUpper(pkt.name); if (name != val) { r.push(`${name} = const(${val})`); if (pkt.packFormat) packFormats[name] = pkt.packFormat; } } } if (desktop && useIdentifiers) r.splice(docsLength + 1, 0, "from jacdac.system.constants import *"); r.push(`${pref}PACK_FORMATS = {`); r.push( Object.keys(packFormats).map((k) => ` ${k}: "${packFormats[k]}"`).join(",\n") ); r.push(`}`); r.push(""); return r.join("\n"); } function toH(info) { let r = "// Autogenerated C header file for " + info.name + "\n"; const hdDef = `_JACDAC_SPEC_${toUpper(info.camelName)}_H`; r += `#ifndef ${hdDef} `; r += `#define ${hdDef} 1 `; let pref = "JD_" + toUpper(info.shortName) + "_"; if (info.shortId[0] == "_") pref = "JD_"; r += ` #define JD_SERVICE_CLASS_${toUpper(info.shortName)} ${toHex( info.classIdentifier )} `; for (const cst in info.constants) { const { value, hex } = info.constants[cst]; r += `#define ${pref}${toUpper(cst)} ${hex ? toHex(value) : value.toString()} `; } for (const en of values(info.enums).filter((en2) => !en2.derived)) { const enPref = pref + toUpper(en.name); r += ` // enum ${en.name} (${cStorage(en.storage)}) `; for (const k of Object.keys(en.members)) r += "#define " + enPref + "_" + toUpper(k) + " " + toHex(en.members[k]) + "\n"; } for (const pkt of info.packets) { if (pkt.derived) continue; const cmt = addComment(pkt); r += wrapComment("h", cmt.comment); if (!pkt.secondary && pkt.kind != "pipe_command" && pkt.kind != "pipe_report") { let inner = "CMD"; if (isRegister(pkt.kind)) inner = "REG"; else if (pkt.kind == "event") inner = "EV"; else if (pkt.kind == "meta_pipe_command" || pkt.kind == "meta_pipe_report") inner = "PIPE"; let val = toHex(pkt.identifier); if (pkt.identifierName) val = "JD_" + inner + "_" + toUpper(pkt.identifierName); const name = pref + inner + "_" + toUpper(pkt.name); if (name != val) r += `#define ${name} ${val} `; } const isMetaPipe = pkt.kind == "meta_pipe_report" || pkt.kind == "meta_pipe_command"; if (cmt.needsStruct || isMetaPipe) { let tname = "jd_" + toLower(info.shortName) + "_" + toLower(pkt.name); if (pkt.kind == "report") tname += "_report"; r += `typedef struct ${tname} { `; if (isMetaPipe) { r += ` uint32_t identifier; // ${toHex(pkt.identifier)} `; } let unaligned = ""; for (let i = 0; i < pkt.fields.length; ++i) { const f = pkt.fields[i]; let def = ""; let cst = cStorage(f.storage); const sz = memberSize(f); if (f.type == "string" || f.type == "string0") def = `char ${f.name}[${sz}]`; else if (cst == "bytes") def = `uint8_t ${f.name}[${sz}]`; else { if (f.isFloat) cst = f.storage == 4 ? "float" : "double"; def = `${cst} ${f.name}`; } if (f.startRepeats && i == pkt.fields.length - 1) def += "[0]"; def += ";"; if (!f.isSimpleType && f.type != "devid") def += " // " + unitPref(f) + f.type; else if (f.unit) def += " // " + prettyUnit(f.unit); r += " " + unaligned + def + "\n"; if (f.type == "string0") unaligned = "// "; } r += `}${packed(pkt)} ${tname}_t; `; } } r += "\n#endif\n"; return r; } function camelize(name) { if (!name) return name; return name[0].toLowerCase() + name.slice(1).replace(/\s+/g, "_").replace(/_([a-z0-9])/gi, (_, l) => l.toUpperCase()); } function capitalize(name) { if (!name) return name; return name[0].toUpperCase() + name.slice(1); } function upperCamel(name) { name = camelize(name); if (!name?.length) return name; return name[0].toUpperCase() + name.slice(1); } function snakify(name) { return name?.replace(/([a-z])([A-Z])/g, (_, a, b) => a + "_" + b).replace(/\s+/g, "_"); } function dashify(name) { if (!name) return name; return snakify(name.replace(/^_+/, "")).replace(/(_|\s)+/g, "-").toLowerCase(); } function humanify(name) { return name?.replace(/([a-z])([A-Z])/g, (_, a, b) => a + " " + b).replace(/(-|_)/g, " "); } function addComment(pkt) { let comment = ""; let typeInfo = ""; let needsStruct = false; if (pkt.fields.length == 0) { if (pkt.kind != "event") typeInfo = "No args"; } else if (pkt.fields.length == 1 && !pkt.fields[0].startRepeats) { const f0 = pkt.fields[0]; typeInfo = cStorage(f0.storage); if (!f0.isSimpleType) typeInfo = f0.type + " (" + typeInfo + ")"; typeInfo = unitPref(f0) + typeInfo; if (f0.name != "_") typeInfo = f0.name + " " + typeInfo; } else { needsStruct = true; } if (pkt.fields.length == 1) { if (isRegister(pkt.kind)) { let info = ""; if (pkt.kind == "ro") info = "Read-only"; else if (pkt.kind == "const") info = "Constant"; else info = "Read-write"; if (typeInfo) typeInfo = info + " " + typeInfo; else typeInfo = info; } else if (typeInfo) { typeInfo = "Argument: " + typeInfo; } } if (pkt.kind == "report" && pkt.secondary) { comment += "Report: " + typeInfo + "\n"; } else { if (pkt.description) { let desc = pkt.description.replace(/\n\n[^]*/, ""); if (typeInfo) desc = typeInfo + ". " + desc; comment = desc + "\n" + comment; } } return { comment, needsStruct }; } function wrapComment(lang, comment) { if (lang === "cs") return "\n/// \n/// " + comment.replace(/\n+$/, "").replace(/\n/g, "\n/// ") + "\n/// \n"; else return "\n/**\n * " + comment.replace(/\n+$/, "").replace(/\n/g, "\n * ") + "\n */\n"; } function wrapSnippet(code) { if (!code) return code; return ` \`\`\` ${code.replace(/^\n+/, "").replace(/\n+$/, "")} \`\`\` `; } var TYPESCRIPT_STATIC_NAMESPACE = "jacdac"; function packFormatForField(info, fld, isStatic, useBooleans) { const sz = memberSize(fld); const szSuff = sz ? `[${sz}]` : ``; let tsType = "number"; let pyType = "float"; let csType = "float"; let fmt = ""; if (/^[fiu]\d+(\.\d+)?$/.test(fld.type) && 1 <= sz && sz <= 8) { fmt = fld.type; if (/^[iu]\d+$/.test(fld.type)) { pyType = "int"; csType = "int"; } if (/^[u]\d+$/.test(fld.type)) { csType = "uint"; } } else if (/^u8\[\d*\]$/.exec(fld.type)) { fmt = "b" + szSuff; } else if (info.enums[fld.type]) { fmt = canonicalType(info.enums[fld.type].storage); pyType = tsType = csType = upperCamel(info.camelName) + upperCamel(fld.type); if (isStatic) tsType = TYPESCRIPT_STATIC_NAMESPACE + "." + tsType; } else { switch (fld.type) { case "string": fmt = "s" + szSuff; csType = tsType = "string"; pyType = "str"; break; case "bytes": fmt = "b" + szSuff; break; case "string0": fmt = "z"; csType = tsType = "string"; pyType = "str"; break; case "devid": fmt = "b[8]"; break; case "pipe_port": fmt = "u16"; break; case "pipe": fmt = "b[12]"; break; case "bool": fmt = "u8"; if (useBooleans) { tsType = "boolean"; csType = pyType = "bool"; } break; default: return null; } } if (tsType == "number" && fmt && fmt[0] == "b") { tsType = "Buffer"; pyType = "bytes"; csType = "byte[]"; } return { fmt, tsType, pyType, csType }; } function packFormat(sinfo, pkt, useBooleans) { if (!pkt.fields?.length) return void 0; const fmt = []; for (const fld of pkt.fields) { if (fld.startRepeats) fmt.push("r:"); const ff = packFormatForField(sinfo, fld, false, useBooleans); if (!ff) return void 0; fmt.push(ff.fmt); } return fmt.join(" "); } function packInfo(info, pkt, options) { const { isStatic = false, useBooleans = false, useJDOM = false } = options || {}; const { kind } = pkt; const vars = []; const vartp = []; const vartppy = []; const vartpcs = []; let fmt = ""; let repeats; let reptp; for (let i = 0; i < pkt.fields.length; ++i) { const fld = pkt.fields[i]; let isArray = ""; if (fld.startRepeats) { if (i == pkt.fields.length - 1) { isArray = "[]"; } else { fmt += "r: "; repeats = []; reptp = []; vars.push("rest"); } } const varname = camelize(fld.name == "_" ? pkt.name : fld.name); const f0 = packFormatForField(info, fld, isStatic, useBooleans); if (!f0 || /(reserved|padding)/.test(fld.name)) { if (!f0) console.log( `${pkt.name}/${fld.name} - can't get format for '${fld.type}'` ); fmt += `x[${memberSize(fld)}] `; } else { fmt += f0.fmt + isArray + " "; let tp = f0.tsType; let tpy = f0.pyType; let tcs = f0.csType; if (tp == "Buffer" && !isStatic) { tp = "Uint8Array"; tpy = "bytes"; tcs = "byte[]"; } tp += isArray; if (isArray) tpy = "[" + tpy + "]"; if (repeats) { repeats.push(varname); reptp.push(tp); } else { vars.push(varname); vartp.push(tp); vartppy.push(tpy); vartpcs.push(tcs); } } } fmt = fmt.replace(/ *$/, ""); if (reptp) vartp.push("([" + reptp.join(", ") + "])[]"); const pktName = camelize(pkt.name); let buffers = ""; if (useJDOM) { if (kind === "command") { for (let i = 0; i < vars.length; ++i) buffers += `const ${vars[i]}: ${vartp[i]} = ... `; buffers += `await service.sendCmdPackedAsync(${capitalize( info.camelName )}Reg.${capitalize(pktName)}, [${vars.join(", ")}]) `; } else if (isRegister(kind)) { buffers += "// get (register to REPORT_UPDATE event to enable background refresh)\n"; buffers += `const ${pktName}Reg = service.register(${capitalize( info.camelName )}Reg.${capitalize(pktName)}) `; buffers += `const [${vars.join(", ")}] : [${vartp.join( ", " )}] = ${pktName}Reg.unpackedValue `; if (kind === "rw") { buffers += "// set\n"; buffers += `await ${pktName}Reg.sendSetPackedAsync([${vars.join( ", " )}]) `; } } else if (kind === "event") { buffers += `const ${pktName}Event = service.event(${capitalize( info.camelName )}Event.${capitalize(pktName)}) `; buffers += `${pktName}Event.on("change", () => { // if you need to read the event values // const values = ${pktName}Event.unpackedValue }) `; } } else { buffers += `const [${vars.join(", ")}] = jdunpack<[${vartp.join( ", " )}]>(buf, "${fmt}") `; } if (repeats) buffers += `const [${repeats.join(", ")}] = rest[0] `; buffers = buffers.replace(/\n*$/, ""); return { buffers, names: vars, types: vartp, pyTypes: vartppy, csTypes: vartpcs }; } function memberSize(fld) { return Math.abs(fld.storage); } function toTypescript(info, language) { const ts = language === "ts"; const sts = language === "sts"; const csharp = language === "cs"; const useNamespace = sts || csharp; const indent = useNamespace ? " " : ""; const indent2 = indent + " "; const numberkw = csharp ? "uint " : ""; const hexkw = csharp ? "byte[]" : ""; const enumkw = csharp ? indent + "public enum" : sts ? indent + "export const enum" : "export enum"; const exportkw = csharp ? "public" : "export"; const enumsf = csharp ? "public const string " : "export const "; const cskw = csharp ? ";" : ""; let r = useNamespace ? `namespace ${csharp ? capitalize(TYPESCRIPT_STATIC_NAMESPACE) : TYPESCRIPT_STATIC_NAMESPACE} { ` : ""; if (csharp) { r += `${indent}public static partial class ServiceClasses ${indent}{ `; } else r += indent + "// Service " + info.name + " constants\n"; if (info.shortId[0] != "_") { const name = csharp ? capitalize(info.camelName) : `SRV_${snakify(info.camelName).toLocaleUpperCase()}`; r += indent + (csharp ? indent : "") + `${exportkw} const ${numberkw}${name} = ${toHex( info.classIdentifier )}${cskw} `; } const pref = upperCamel(info.camelName); for (const cst in info.constants) { const name = csharp ? capitalize(info.camelName) : `CONST_${snakify(info.camelName).toLocaleUpperCase()}_`; const { value, hex } = info.constants[cst]; r += indent + (csharp ? indent : "") + `${exportkw} const ${hex ? hexkw : numberkw}${name}${csharp ? capitalize(camelize(cst)) : toUpper(cst)} = ${hex ? value.toString() : toHex(value)}${cskw} `; } if (csharp) { r += indent + `} `; } for (const en of values(info.enums)) { const enPref = pref + upperCamel(en.name); r += ` ${csharp && en.isFlags ? " [System.Flags]\n" : ""}${enumkw} ${enPref}${csharp ? `: ${cSharpStorage(en.storage)}` : ""} { // ${cStorage(en.storage)} `; for (const k of Object.keys(en.members)) { if (sts) r += indent2 + `//% block="${humanify(k).toLowerCase()}" `; r += indent2 + k + " = " + toHex(en.members[k]) + ",\n"; } r += indent + "}\n\n"; } const tsEnums = {}; for (const pkt of info.packets) { if (pkt.derived) continue; const cmt = addComment(pkt); const pack = pkt.fields.length ? packInfo(info, pkt, { isStatic: sts, useBooleans: false }).buffers : ""; let inner = "Cmd"; if (isRegister(pkt.kind)) inner = "Reg"; else if (pkt.kind == "event") inner = "Event"; else if (pkt.kind == "meta_pipe_command" || pkt.kind == "meta_pipe_report") inner = "PipeCmd"; else if (pkt.kind == "pipe_command" || pkt.kind == "pipe_report") inner = "Pipe"; let text = ""; let meta = ""; if (pkt.secondary || inner == "Pipe") { if (pack) text = wrapComment( language, `${pkt.kind} ${upperCamel(pkt.name)}${pkt.client ? "" : wrapSnippet(pack)}` ); } else { const val = toHex(pkt.identifier); if (sts && pkt.kind === "event") { meta = `//% block="${snakify(pkt.name).replace(/_/g, " ")}" `; } text = `${wrapComment( language, cmt.comment + (pkt.client ? "" : wrapSnippet(pack)) ) + meta}${upperCamel(pkt.name)} = ${val}, `; } if (text) tsEnums[inner] = (tsEnums[inner] || "") + text; if ((csharp || sts || ts) && pkt.packFormat) { const packName = inner + "Pack"; tsEnums[packName] = (tsEnums[packName] || "") + `${wrapComment( language, `Pack format for '${pkt.name}' data.` )}${enumsf}${upperCamel(pkt.name)}${pkt.secondary ? "Report" : ""} = "${pkt.packFormat}"${csharp ? ";" : ""} `; } } for (const k of Object.keys(tsEnums)) { if (k == "info") r += tsEnums[k].replace(/^/gm, indent) + "\n\n"; else { const inner = tsEnums[k].replace(/^\n+/, "").replace(/\n$/, "").replace(/\n/g, "\n " + indent); if (inner.indexOf("public const") > -1 || k.endsWith("Pack")) { r += ` ${exportkw} ${csharp ? "static " : ""}${csharp ? "class" : "namespace"} ${pref}${k} { ${indent}${inner} ${indent}} `; } else r += `${enumkw} ${pref}${k} ${csharp ? `: ushort ` : ""}{ ${indent}${inner} ${indent}} `; } } if (useNamespace) r += "}\n"; return r.replace(/ *$/gm, ""); } var jsKeywords = { switch: 1 }; function jsQuote(n) { if (jsKeywords[n]) n += "_"; return n; } function generateDeviceSpecificationId(dev) { return escapeDeviceIdentifier(dev.company) + "-" + escapeDeviceNameIdentifier(dev.name) + (dev.designIdentifier || "") + (dev.version ? `v${dev.version.toLowerCase().replace(/^v/, "").replace(/\./g, "")}` : "").toLowerCase(); } function normalizeDeviceSpecification(dev) { const productIdentifiers = Array.from( (/* @__PURE__ */ new Set([ ...dev.productIdentifiers || [], ...dev.firmwares?.map((fw) => fw.productIdentifier).filter((pi) => !!pi) || [] ])).values() ); const clone2 = { id: generateDeviceSpecificationId(dev), name: dev.name, company: dev.company, description: dev.description, repo: dev.repo, makeCodeRepo: dev.makeCodeRepo, firmwareSource: dev.firmwareSource, hardwareDesign: dev.hardwareDesign, connector: dev.connector, link: dev.link, storeLink: dev.storeLink, services: dev.services, productIdentifiers, transport: dev.transport, tags: dev.tags, firmwares: dev.firmwares, version: dev.version ? dev.version.replace(/^v/, "") : void 0, designIdentifier: dev.designIdentifier || void 0, bootloader: dev.bootloader, status: dev.status || (dev.storeLink ? "stable" : void 0), devices: dev.devices, relatedDevices: dev.relatedDevices, requiredDevices: dev.requiredDevices, shape: dev.shape, order: dev.order }; const anyClone = clone2; for (const key of Object.keys(anyClone)) { const v = anyClone[key]; if (v === void 0 || v === "" || Array.isArray(v) && !v.length) delete anyClone[key]; } return clone2; } function escapeDeviceIdentifier(text) { if (!text) text = ""; const escaped = text.trim().toLowerCase().replace(/([^a-z0-9_-])+/gi, "-").replace(/\./g, "").replace(/^-+/, "").replace(/-+$/, ""); const id = snakify(escaped); return id; } function escapeDeviceNameIdentifier(text) { return escapeDeviceIdentifier(text).replace(/-/g, ""); } function converters() { return { json: (j) => JSON.stringify(j, null, 2), c: toH, ts: (j) => toTypescript(j, "ts"), sts: (j) => toTypescript(j, "sts"), cs: (j) => toTypescript(j, "cs"), py: (j) => toPython(j, "py") /* "cpp": toHPP, */ }; } function isNumericType(field) { const tp = field.type; return !field.startRepeats && /^[uif]\d+(\.\d+)?$/.test(tp) && tp != "pipe_port" && tp != "bool"; } var Reading = 257; function genFieldInfo(reg, field) { const isReading2 = reg.identifier === Reading; const name = field.name === "_" ? reg.name : isReading2 ? field.name : `${reg.name}${capitalize(field.name)}`; const min = pick2( field.typicalMin, field.absoluteMin, field.unit === "/" || /^%/.test(field.unit) ? field.type[0] === "i" ? -100 : 0 : void 0, field.type === "u8" || field.type === "u16" ? 0 : void 0 ); const max = pick2( field.typicalMax, field.absoluteMax, field.unit === "/" || /^%/.test(field.unit) ? 100 : void 0, field.type === "u8" ? 255 : field.type === "u16" ? 65535 : void 0 ); const defl = field.defaultValue || (field.unit === "/" ? "100" : void 0); const valueScaler = field.unit === "/" ? (s) => `${s} * 100` : field.type === "bool" ? (s) => `!!${s}` : (s) => s; const valueUnscaler = field.unit === "/" ? (s) => `${s} / 100` : field.type === "bool" ? (s) => `${s} ? 1 : 0` : (s) => s; const scale = field.unit === "/" ? 100 : void 0; let unit = field.unit === "/" ? "%" : field.unit; unit = unit?.replace("%", "\\\\%"); return { name, min, max, defl, scale, valueScaler, valueUnscaler, unit }; function pick2(...values2) { return values2?.find((x) => x !== void 0); } } // src/jdom/error.ts var JDError = class extends Error { constructor(message, options) { super(message); this.name = JACDAC_ERROR; this.code = options?.code; this.cancel = !!options?.cancel; } }; function throwError(msg, options) { const e = new JDError(msg, options); throw e; } function isCancelError(e) { const res = e?.name === JACDAC_ERROR ? e?.cancel : false; return res; } function isAckError(e) { return isCodeError(e, ERROR_NO_ACK); } function isTimeoutError(e) { return isCodeError(e, ERROR_TIMEOUT); } function isCodeError(e, code) { return errorCode(e) === code; } function errorCode(e) { const code = e?.name === JACDAC_ERROR ? e?.code : void 0; if (code) return code; const deviceLocked = e.name == "NetworkError" && /unable to claim interface/i.test(e.message); if (deviceLocked) return ERROR_TRANSPORT_DEVICE_LOCKED; return void 0; } // src/jdom/flags.ts var Flags = class { }; /** * Enables additional logging and diagnostics */ Flags.diagnostics = false; /** * Trace who and what generates packets */ Flags.trace = false; /** * Enables/disabled WebUSB */ Flags.webUSB = true; /** * Enables/disabled WebSerial */ Flags.webSerial = true; /** * Enables/disables WebBLE */ Flags.webBluetooth = false; /** * Enables developer mode when connecting devices */ Flags.developerMode = false; // src/jdom/utils.ts function arrayify(value) { if (value === void 0 || value === null) return void 0; if (Array.isArray(value)) return value; else return [value]; } function delay(millis, value) { return new Promise((resolve) => setTimeout(() => resolve(value), millis)); } function memcpy(trg, trgOff, src, srcOff, len) { if (srcOff === void 0) srcOff = 0; if (len === void 0) len = src.length - srcOff; for (let i = 0; i < len; ++i) trg[trgOff + i] = src[srcOff + i]; } function strcmp(a, b) { if (a == b) return 0; if (a < b) return -1; else return 1; } function bufferEq(a, b, offset = 0) { if (a == b) return true; if (!a || !b || a.length != b.length) return false; for (let i = offset; i < a.length; ++i) { if (a[i] != b[i]) return false; } return true; } function arrayEq(a, b, isEqual) { const eq = isEqual || Object.is; return a?.length === b?.length && (!a || a.every((_, i) => eq(_, b[i]))); } function hash(buf, bits) { bits |= 0; if (bits < 1) return 0; const h = fnv1(buf); if (bits >= 32) return h >>> 0; else return ((h ^ h >>> bits) & (1 << bits) - 1) >>> 0; } function idiv(a, b) { return (a | 0) / (b | 0) | 0; } function fnv1(data) { let h = 2166136261; for (let i = 0; i < data.length; ++i) { h = Math.imul(h, 16777619) ^ data[i]; } return h; } function crc(p) { let crc2 = 65535; for (let i = 0; i < p.length; ++i) { const data = p[i]; let x = crc2 >> 8 ^ data; x ^= x >> 4; crc2 = crc2 << 8 ^ x << 12 ^ x << 5 ^ x; crc2 &= 65535; } return crc2; } function ALIGN(n) { return n + 3 & ~3; } function stringToUint8Array(input) { const len = input.length; const res = new Uint8Array(len); for (let i = 0; i < len; ++i) res[i] = input.charCodeAt(i) & 255; return res; } function uint8ArrayToString(input) { const len = input.length; let res = ""; for (let i = 0; i < len; ++i) res += String.fromCharCode(input[i]); return res; } function fromUTF8(binstr) { if (!binstr) return ""; let escaped = ""; for (let i = 0; i < binstr.length; ++i) { const k = binstr.charCodeAt(i) & 255; if (k == 37 || k > 127) { escaped += "%" + k.toString(16); } else { escaped += binstr.charAt(i); } } return decodeURIComponent(escaped); } function toUTF8(str, cesu8) { let res = ""; if (!str) return res; for (let i = 0; i < str.length; ++i) { let code = str.charCodeAt(i); if (code <= 127) res += str.charAt(i); else if (code <= 2047) { res += String.fromCharCode(192 | code >> 6, 128 | code & 63); } else { if (!cesu8 && 55296 <= code && code <= 56319) { const next = str.charCodeAt(++i); if (!isNaN(next)) code = 65536 + (code - 55296 << 10) + (next - 56320); } if (code <= 65535) res += String.fromCharCode( 224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63 ); else res += String.fromCharCode( 240 | code >> 18, 128 | code >> 12 & 63, 128 | code >> 6 & 63, 128 | code & 63 ); } } return res; } var PromiseBuffer = class { constructor() { this.waiting = []; this.available = []; } drain() { for (const f of this.waiting) { f(new Error("Promise Buffer Reset")); } this.waiting = []; this.available = []; } pushError(v) { this.push(v); } push(v) { const f = this.waiting.shift(); if (f) f(v); else this.available.push(v); } shiftAsync(timeout = 0) { if (this.available.length > 0) { const v = this.available.shift(); if (v instanceof Error) return Promise.reject(v); else return Promise.resolve(v); } else return new Promise((resolve, reject) => { const f = (v) => { if (v instanceof Error) reject(v); else resolve(v); }; this.waiting.push(f); if (timeout > 0) { delay(timeout).then(() => { const idx = this.waiting.indexOf(f); if (idx >= 0) { this.waiting.splice(idx, 1); reject( new JDError(`Timeout (${timeout}ms)`, { code: ERROR_TIMEOUT }) ); } }); } }); } }; var PromiseQueue = class { constructor() { // eslint-disable-next-line @typescript-eslint/no-explicit-any this.promises = {}; } enqueue(id, f) { return new Promise((resolve, reject) => { let arr = this.promises[id]; if (!arr) { arr = this.promises[id] = []; } const cleanup = () => { arr.shift(); if (arr.length == 0) delete this.promises[id]; else arr[0](); }; arr.push( () => f().then( (v) => { cleanup(); resolve(v); }, (err) => { cleanup(); reject(err); } ) ); if (arr.length == 1) arr[0](); }); } }; function rgbToHtmlColor(rgb2) { return `#${("000000" + rgb2.toString(16)).slice(-6)}`; } function rgbaToHtmlColor(rgb2, a) { return `rgba(${rgb2 >> 16 & 255}, ${rgb2 >> 8 & 255}, ${rgb2 >> 0 & 255}, ${a})`; } function toFullHex(n) { return "0x" + n.map((id) => ("000000000" + id.toString(16)).slice(-8)).join(""); } function toHex2(bytes, sep) { if (!bytes) return void 0; let r = ""; for (let i = 0; i < bytes.length; ++i) { if (sep && i > 0) r += sep; r += ("0" + bytes[i].toString(16)).slice(-2); } return r; } function fromHex(hex) { const r = new Uint8Array(hex.length >> 1); for (let i = 0; i < hex.length; i += 2) r[i >> 1] = parseInt(hex.slice(i, i + 2), 16); return r; } function isSet(v) { return v !== null && v !== void 0; } function toArray(a) { if (!a) return void 0; const r = new Array(a.length); for (let i = 0; i < a.length; ++i) r[i] = a[i]; return r; } function hexNum(n) { if (isNaN(n)) return void 0; if (n < 0) return "-" + hexNum(-n); return "0x" + n.toString(16); } function write32(buf, pos, v) { buf[pos + 0] = v >> 0 & 255; buf[pos + 1] = v >> 8 & 255; buf[pos + 2] = v >> 16 & 255; buf[pos + 3] = v >> 24 & 255; } function write24(buf, pos, v) { buf[pos + 0] = v >> 0 & 255; buf[pos + 1] = v >> 8 & 255; buf[pos + 2] = v >> 16 & 255; } function write16(buf, pos, v) { buf[pos + 0] = v >> 0 & 255; buf[pos + 1] = v >> 8 & 255; } function read32(buf, pos) { return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16 | buf[pos + 3] << 24) >>> 0; } function read16(buf, pos) { return buf[pos] | buf[pos + 1] << 8; } function encodeU32LE(words) { const r = new Uint8Array(words.length * 4); for (let i = 0; i < words.length; ++i) write32(r, i * 4, words[i]); return r; } function decodeU32LE(buf) { const res = []; for (let i = 0; i < buf.length; i += 4) res.push(read32(buf, i)); return res; } function isBufferEmpty(data) { if (!data) return true; const n = data.length; for (let i = 0; i < n; ++i) { if (data[i]) return false; } return true; } function bufferToString(buf) { return fromUTF8(uint8ArrayToString(buf)); } function stringToBuffer(str) { return stringToUint8Array(toUTF8(str)); } function bufferConcat(a, b) { const r = new Uint8Array(a.length + b.length); r.set(a, 0); r.set(b, a.length); return r; } function bufferConcatMany(bufs) { let sz = 0; for (const buf of bufs) sz += buf.length; const r = new Uint8Array(sz); sz = 0; for (const buf of bufs) { r.set(buf, sz); sz += buf.length; } return r; } function arrayConcatMany(arrs) { if (!arrs) return void 0; arrs = arrs.filter((a) => !!a?.length); let sz = 0; for (const buf of arrs) sz += buf.length; const r = new Array(sz); sz = 0; for (const arr of arrs) { for (let i = 0; i < arr.length; ++i) r[i + sz] = arr[i]; sz += arr.length; } return r; } function jsonCopyFrom(trg, src) { const v = clone(src); for (const k of Object.keys(src)) { ; trg[k] = v[k]; } } function assert(cond, msg = "Assertion failed", debugData) { if (!cond) { if (debugData) console.debug(`assertion filed ${msg}`, debugData); if (Flags.diagnostics) debugger; throw new Error(msg); } } function flatClone(obj) { if (obj == null) return null; const r = {}; Object.keys(obj).forEach((k) => { r[k] = obj[k]; }); return r; } function clone(v) { if (v == null) return null; return JSON.parse(JSON.stringify(v)); } function throttle(handler, delay2) { let enableCall = true; return function() { if (!enableCall) return; enableCall = false; handler(); setTimeout(() => enableCall = true, delay2); }; } function signal() { let resolve; return { signalled: new Promise((r) => { resolve = r; }), signal: () => resolve(true) }; } function readBlobToUint8Array(blob) { if (blob?.arrayBuffer) { return blob.arrayBuffer().then((data) => new Uint8Array(data)); } return new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = () => { resolve(new Uint8Array(fileReader.result)); }; fileReader.onerror = (e) => { reject(e); }; try { fileReader.readAsArrayBuffer(blob); } catch (e) { reject(e); } }); } function readBlobToText(blob) { if (blob.text) { return blob.text(); } return new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = () => resolve(fileReader.result); fileReader.onerror = (e) => { reject(e); }; try { fileReader.readAsText(blob); } catch (e) { reject(e); } }); } function debounce(handler, delay2) { let timeOutId; return function() { if (timeOutId) { clearTimeout(timeOutId); } timeOutId = setTimeout(async () => { handler(); }, delay2); }; } function debounceAsync(handler, delay2) { let timeOutId; return function() { if (timeOutId) { clearTimeout(timeOutId); } timeOutId = setTimeout(async () => { await handler(); }, delay2); }; } function JSONTryParse(src, defaultValue) { if (src === void 0) return void 0; if (src === null) return null; try { return JSON.parse(src); } catch (e) { return defaultValue; } } function roundWithPrecision(x, digits, round = Math.round) { digits = digits | 0; if (digits <= 0) return round(x); if (x == 0) return 0; let r = 0; while (r == 0 && digits < 21) { const d = Math.pow(10, digits++); r = round(x * d + Number.EPSILON) / d; } return r; } function renderWithPrecision(x, digits, round = Math.round) { const r = roundWithPrecision(x, digits, round); let rs = r.toLocaleString(); const sep = 0.1 .toLocaleString()[1]; if (digits > 0) { let doti = rs.indexOf(sep); if (doti < 0) { rs += sep; doti = rs.length - 1; } while (rs.length - 1 - doti < digits) rs += "0"; } return rs; } function randomRange(min, max) { return Math.round(Math.random() * (max - min) + min); } function unique(values2) { return Array.from(new Set(values2).values()); } function uniqueMap(values2, id, converter) { const r = {}; for (let i = 0; i < values2.length; ++i) { const value = values2[i]; const idv = id(value); if (!r[idv]) { r[idv] = value; } } return Object.values(r).map(converter); } function toMap(a, keyConverter, valueConverter, ignoreMissingValues) { const m = {}; if (a) for (let i = 0; i < a.length; ++i) { const key = keyConverter(a[i], i); if (key === void 0 || key === null) continue; const v = valueConverter(a[i], i); if (ignoreMissingValues && (v === void 0 || v === null)) continue; m[key] = v; } return m; } function ellipse(text, maxChars, suffix = "\u2026") { if (!isNaN(maxChars) && maxChars > 0 && text?.length > maxChars) return text.slice(0, maxChars - suffix.length) + suffix; return text; } function ellipseFirstSentence(text) { const i = text?.indexOf("."); if (i < 0) return text; else return text.slice(0, i + 1); } function ellipseJoin(values2, maxChars, ellipse2 = "...") { let r = ""; for (let i = 0; i < values2.length && r.length < maxChars; ++i) { if (r) r += ", "; r += values2[i]; } if (r.length > maxChars - ellipse2.length) return r.slice(0, maxChars) + ellipse2; else return r; } function arrayShuffle(a) { for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; } function uniqueName(names, name, separator = "", startCount = 2) { if (names.indexOf(name) < 0) return name; let count = startCount; while (names.indexOf(`${name}${separator}${count}`) > -1) count++; return `${name}${separator}${count}`; } function groupBy(list, key) { if (!list) return {}; const r = {}; list.forEach((item) => { const k = key(item); const a = r[k] || (r[k] = []); a.push(item); }); return r; } function pick(...values2) { return values2?.find((x) => x !== void 0); } function splitFilter(values2, condition) { if (!values2) return [void 0, void 0]; const yays = []; const nays = []; const n = values2.length; for (let i = 0; i < n; ++i) { const v = values2[i]; if (condition(v)) yays.push(v); else nays.push(v); } return [yays, nays]; } function range(end) { return Array(end).fill(0).map((_, i) => i); } function toggleBit(data, bitindex) { data[bitindex >> 3] ^= 1 << (bitindex & 7); } function getBit(data, bitindex) { return !!(data[bitindex >> 3] & 1 << (bitindex & 7)); } function setBit(data, bitindex, on) { if (on) data[bitindex >> 3] |= 1 << (bitindex & 7); else data[bitindex >> 3] &= ~(1 << (bitindex & 7)); } function parseIdentifier(value) { if (typeof value === "string" && /^0x[0-9a-f]+$/i.test(value)) { return parseInt(value, 16); } else if (typeof value === "string" && /^[0-9]+$/i.test(value)) return parseInt(value); return Number(value); } // src/jdom/random.ts function cryptoRandomUint32(length) { const crypto = (typeof self !== "undefined" ? self.crypto : void 0) || (typeof globalThis !== "undefined" ? globalThis.crypto : void 0); if (!crypto) return void 0; const vals = new Uint32Array(length); crypto.getRandomValues(vals); return vals; } function anyRandomUint32(length) { let r = cryptoRandomUint32(length); if (!r) { r = new Uint32Array(length); for (let i = 0; i < r.length; ++i) r[i] = Math.random() * 4294967296 >>> 0; } return r; } function randomUInt(max) { const arr = anyRandomUint32(1); return arr[0] % max; } function randomBytes(n) { const buf = anyRandomUint32(n); const r = new Uint8Array(buf.length); for (let i = 0; i < n; ++i) r[i] = buf[i] & 255; return r; } function randomDeviceId() { const devId = anyRandomUint32(8); for (let i = 0; i < 8; ++i) devId[i] &= 255; return toHex2(devId); } // src/jdom/buffer.ts var NumberFormat = /* @__PURE__ */ ((NumberFormat2) => { NumberFormat2[NumberFormat2["Int8LE"] = 1] = "Int8LE"; NumberFormat2[NumberFormat2["UInt8LE"] = 2] = "UInt8LE"; NumberFormat2[NumberFormat2["Int16LE"] = 3] = "Int16LE"; NumberFormat2[NumberFormat2["UInt16LE"] = 4] = "UInt16LE"; NumberFormat2[NumberFormat2["Int32LE"] = 5] = "Int32LE"; NumberFormat2[NumberFormat2["Int8BE"] = 6] = "Int8BE"; NumberFormat2[NumberFormat2["UInt8BE"] = 7] = "UInt8BE"; NumberFormat2[NumberFormat2["Int16BE"] = 8] = "Int16BE"; NumberFormat2[NumberFormat2["UInt16BE"] = 9] = "UInt16BE"; NumberFormat2[NumberFormat2["Int32BE"] = 10] = "Int32BE"; NumberFormat2[NumberFormat2["UInt32LE"] = 11] = "UInt32LE"; NumberFormat2[NumberFormat2["UInt32BE"] = 12] = "UInt32BE"; NumberFormat2[NumberFormat2["Float32LE"] = 13] = "Float32LE"; NumberFormat2[NumberFormat2["Float64LE"] = 14] = "Float64LE"; NumberFormat2[NumberFormat2["Float32BE"] = 15] = "Float32BE"; NumberFormat2[NumberFormat2["Float64BE"] = 16] = "Float64BE"; NumberFormat2[NumberFormat2["UInt64LE"] = 17] = "UInt64LE"; NumberFormat2[NumberFormat2["UInt64BE"] = 18] = "UInt64BE"; NumberFormat2[NumberFormat2["Int64LE"] = 19] = "Int64LE"; NumberFormat2[NumberFormat2["Int64BE"] = 20] = "Int64BE"; return NumberFormat2; })(NumberFormat || {}); function fmtInfoCore(fmt) { switch (fmt) { case 1 /* Int8LE */: return -1; case 2 /* UInt8LE */: return 1; case 3 /* Int16LE */: return -2; case 4 /* UInt16LE */: return 2; case 5 /* Int32LE */: return -4; case 11 /* UInt32LE */: return 4; case 19 /* Int64LE */: return -8; case 17 /* UInt64LE */: return 8; case 6 /* Int8BE */: return -10; case 7 /* UInt8BE */: return 10; case 8 /* Int16BE */: return -20; case 9 /* UInt16BE */: return 20; case 10 /* Int32BE */: return -40; case 12 /* UInt32BE */: return 40; case 20 /* Int64BE */: return -80; case 18 /* UInt64BE */: return 80; case 13 /* Float32LE */: return 4; case 15 /* Float32BE */: return 40; case 14 /* Float64LE */: return 8; case 16 /* Float64BE */: return 80; default: throw new Error("unknown format"); } } function fmtInfo(fmt) { let size = fmtInfoCore(fmt); let signed = false; if (size < 0) { signed = true; size = -size; } let swap = false; if (size >= 10) { swap = true; size /= 10; } let isFloat = false; switch (fmt) { case 13 /* Float32LE */: case 15 /* Float32BE */: case 14 /* Float64LE */: case 16 /* Float64BE */: isFloat = true; break; } return { size, signed, swap, isFloat }; } function sizeOfNumberFormat(format) { switch (format) { case 1 /* Int8LE */: case 2 /* UInt8LE */: case 6 /* Int8BE */: case 7 /* UInt8BE */: return 1; case 3 /* Int16LE */: case 4 /* UInt16LE */: case 8 /* Int16BE */: case 9 /* UInt16BE */: return 2; case 5 /* Int32LE */: case 10 /* Int32BE */: case 12 /* UInt32BE */: case 11 /* UInt32LE */: case 15 /* Float32BE */: case 13 /* Float32LE */: return 4; case 18 /* UInt64BE */: case 20 /* Int64BE */: case 17 /* UInt64LE */: case 19 /* Int64LE */: case 16 /* Float64BE */: case 14 /* Float64LE */: return 8; } return 0; } function getNumber(buf, fmt, offset) { switch (fmt) { case 7 /* UInt8BE */: case 2 /* UInt8LE */: return buf[offset]; case 6 /* Int8BE */: case 1 /* Int8LE */: return buf[offset] << 24 >> 24; case 4 /* UInt16LE */: return read16(buf, offset); case 3 /* Int16LE */: return read16(buf, offset) << 16 >> 16; case 11 /* UInt32LE */: return read32(buf, offset); case 5 /* Int32LE */: return read32(buf, offset) >> 0; case 17 /* UInt64LE */: return read32(buf, offset) + read32(buf, offset + 4) * 4294967296; case 19 /* Int64LE */: return read32(buf, offset) + (read32(buf, offset + 4) >> 0) * 4294967296; default: { const inf = fmtInfo(fmt); if (inf.isFloat) { const arr = new Uint8Array(inf.size); for (let i = 0; i < inf.size; ++i) { arr[i] = buf[offset + i]; } if (inf.swap) arr.reverse(); if (inf.size == 4) return new Float32Array(arr.buffer)[0]; else return new Float64Array(arr.buffer)[0]; } throw new Error("unsupported fmt:" + fmt); } } } function setNumber(buf, fmt, offset, r) { const inf = fmtInfo(fmt); if (inf.isFloat) { const arr = new Uint8Array(inf.size); if (inf.size == 4) new Float32Array(arr.buffer)[0] = r; else new Float64Array(arr.buffer)[0] = r; if (inf.swap) arr.reverse(); for (let i = 0; i < inf.size; ++i) { buf[offset + i] = arr[i]; } return; } if (fmt == 17 /* UInt64LE */ || fmt == 19 /* Int64LE */) { setNumber(buf, 11 /* UInt32LE */, offset, r >>> 0); setNumber(buf, 11 /* UInt32LE */, offset + 4, r / 4294967296); return; } for (let i = 0; i < inf.size; ++i) { const off = !inf.swap ? offset + i : offset + inf.size - i - 1; buf[off] = r & 255; r >>= 8; } } function uintOfBuffer(data) { let fmt; switch (data.length) { case 0: case 1: fmt = 2 /* UInt8LE */; break; case 2: case 3: fmt = 4 /* UInt16LE */; break; default: fmt = 11 /* UInt32LE */; break; } return getNumber(data, fmt, 0); } function intOfBuffer(data) { let fmt; switch (data.length) { case 0: case 1: fmt = 1 /* Int8LE */; break; case 2: case 3: fmt = 3 /* Int16LE */; break; default: fmt = 5 /* Int32LE */; break; } return getNumber(data, fmt, 0); } function bufferToArray(data, fmt) { const res = []; const sz = sizeOfNumberFormat(fmt); for (let off = 0; off <= data.length - sz; off += sz) res.push(getNumber(data, fmt, off)); return res; } function concatBufferArray(chunks) { let sz = 0; for (const ch of chunks) sz += ch.length; const r = new Uint8Array(sz); sz = 0; for (const ch of chunks) { r.set(ch, sz); sz += ch.length; } return r; } async function sha256(buffers) { const concat = concatBufferArray(buffers); if (typeof self === "undefined" || !window.crypto) { const s = require("crypto").createHash("sha256"); s.update(concat); return Promise.resolve(new Uint8Array(s.digest())); } const r = await self.crypto.subtle.digest("SHA-256", concat); return new Uint8Array(r); } async function sha256Hmac(key, msg) { const blockSize = 64; if (key.length > blockSize) key = await sha256([key]); const paddedKey = new Uint8Array(blockSize); paddedKey.set(key, 0); for (let i = 0; i < blockSize; ++i) paddedKey[i] ^= 54; const h0 = await sha256([paddedKey, msg]); for (let i = 0; i < blockSize; ++i) paddedKey[i] ^= 54 ^ 92; return await sha256([paddedKey, h0]); } function fromBase64(encoded) { if (typeof Buffer == "function" && typeof Buffer.from == "function") return new Uint8Array(Buffer.from(encoded, "base64")); else return stringToUint8Array(atob(encoded)); } function toBase64(data) { if (typeof Buffer == "function" && typeof Buffer.from == "function") return Buffer.from(data).toString("base64"); else return btoa(uint8ArrayToString(data)); } // jacdac-spec/dist/services.json var services_default = [{ name: "Common registers and commands", status: "experimental", shortId: "_system", camelName: "system", shortName: "system", extends: [], notes: { short: "This file describes common register and command codes.\n\nThese are defined in ranges separate from the per-service ones.\nNo service actually derives from this file, but services can include packets\ndefined here.\nTheir code is listed as say `@ intensity` and not `@ 0x01` (the spectool enforces that).", commands: "Command codes are subdivided as follows:\n\n- Commands `0x000-0x07f` - common to all services\n- Commands `0x080-0xeff` - defined per-service\n- Commands `0xf00-0xfff` - reserved for implementation\n\nCommands follow.", registers: "Register codes are subdivided as follows:\n\n- Registers `0x001-0x07f` - r/w common to all services\n- Registers `0x080-0x0ff` - r/w defined per-service\n- Registers `0x100-0x17f` - r/o common to all services\n- Registers `0x180-0x1ff` - r/o defined per-service\n- Registers `0x200-0xeff` - custom, defined per-service\n- Registers `0xf00-0xfff` - reserved for implementation, should not be seen on the wire\n\nThe types listed are typical. Check spec for particular service for exact type,\nand a service-specific name for a register (eg. `value` could be `pulse_length`).\nAll registers default to `0` unless otherwise indicated.", events: "Events codes are 8-bit and are subdivided as follows:\n\n- Events `0x00-0x7f` - common to all services\n- Events `0x80-0xff` - defined per-service" }, classIdentifier: 536870897, enums: { ReadingThreshold: { name: "ReadingThreshold", storage: 1, members: { Neutral: 1, Inactive: 2, Active: 3 } }, StatusCodes: { name: "StatusCodes", storage: 2, members: { Ready: 0, Initializing: 1, Calibrating: 2, Sleeping: 3, WaitingForInput: 4, CalibrationNeeded: 100 } } }, constants: { announce_interval: { value: 500, hex: false } }, packets: [{ kind: "command", name: "announce", identifier: 0, description: "Enumeration data for control service; service-specific advertisement data otherwise.\nControl broadcasts it automatically every `announce_interval`ms, but other service have to be queried to provide it.", fields: [], hasReport: true }, { kind: "report", name: "announce", identifier: 0, description: "Enumeration data for control service; service-specific advertisement data otherwise.\nControl broadcasts it automatically every `announce_interval`ms, but other service have to be queried to provide it.", fields: [], secondary: true }, { kind: "command", name: "get_register", identifier: 4096, description: "Registers number `N` is fetched by issuing command `0x1000 | N`.\nThe report format is the same as the format of the register.", fields: [], hasReport: true }, { kind: "report", name: "get_register", identifier: 4096, description: "Registers number `N` is fetched by issuing command `0x1000 | N`.\nThe report format is the same as the format of the register.", fields: [], secondary: true }, { kind: "command", name: "set_register", identifier: 8192, description: "Registers number `N` is set by issuing command `0x2000 | N`, with the format\nthe same as the format of the register.", fields: [] }, { kind: "command", name: "calibrate", identifier: 2, description: "Request to calibrate a sensor. The report indicates the calibration is done.", fields: [], hasReport: true }, { kind: "report", name: "calibrate", identifier: 2, description: "Request to calibrate a sensor. The report indicates the calibration is done.", fields: [], secondary: true }, { kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16 u16" }, { kind: "rw", name: "intensity", identifier: 1, description: "This is either binary on/off (0 or non-zero), or can be gradual (eg. brightness of an RGB LED strip).", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "rw", name: "value", identifier: 2, description: "The primary value of actuator (eg. servo pulse length, or motor duty cycle).", fields: [{ name: "_", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "const", name: "min_value", identifier: 272, description: "The lowest value that can be reported for the value register.", fields: [{ name: "_", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "const", name: "max_value", identifier: 273, description: "The highest value that can be reported for the value register.", fields: [{ name: "_", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "rw", name: "max_power", identifier: 7, description: "Limit the power drawn by the service, in mA.", fields: [{ name: "_", unit: "mA", type: "u16", storage: 2, isSimpleType: true, defaultValue: 500, typicalMax: 500, typicalMin: 0 }], packFormat: "u16" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100 }], packFormat: "u32" }, { kind: "ro", name: "reading", identifier: 257, description: "Read-only value of the sensor, also reported in streaming.", fields: [{ name: "_", type: "i32", storage: -4, isSimpleType: true }], volatile: true, packFormat: "i32" }, { kind: "rw", name: "reading_range", identifier: 8, description: "For sensors that support it, sets the range (sometimes also described `min`/`max_reading`).\nTypically only a small set of values is supported.\nSetting it to `X` will select the smallest possible range that is at least `X`,\nor if it doesn't exist, the largest supported range.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "const", name: "supported_ranges", identifier: 266, description: "Lists the values supported as `reading_range`.", fields: [{ name: "range", type: "u32", storage: 4, isSimpleType: true, startRepeats: true }], packFormat: "r: u32" }, { kind: "const", name: "min_reading", identifier: 260, description: "The lowest value that can be reported by the sensor.", fields: [{ name: "_", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "const", name: "max_reading", identifier: 261, description: "The highest value that can be reported by the sensor.", fields: [{ name: "_", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "ro", name: "reading_error", identifier: 262, description: "The real value of whatever is measured is between `reading - reading_error` and `reading + reading_error`. It should be computed from the internal state of the sensor. This register is often, but not always `const`. If the register value is modified,\nsend a report in the same frame of the `reading` report.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], volatile: true, packFormat: "u32" }, { kind: "const", name: "reading_resolution", identifier: 264, description: "Smallest, yet distinguishable change in reading.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "rw", name: "inactive_threshold", identifier: 5, description: "Threshold when reading data gets inactive and triggers a `inactive`.", fields: [{ name: "_", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "rw", name: "active_threshold", identifier: 6, description: "Thresholds when reading data gets active and triggers a `active` event.", fields: [{ name: "_", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "const", name: "variant", identifier: 263, description: "The hardware variant of the service.\nFor services which support this, there's an enum defining the meaning.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], packFormat: "s" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. `code` is a standardized value from\nthe Jacdac status/error codes. `vendor_code` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet.", fields: [{ name: "code", type: "StatusCodes", storage: 2 }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, packFormat: "u16 u16" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, packFormat: "s" }, { kind: "event", name: "active", identifier: 1, description: "Notifies that the service has been activated (eg. button pressed, network connected, etc.)", fields: [] }, { kind: "event", name: "inactive", identifier: 2, description: "Notifies that the service has been dis-activated.", fields: [] }, { kind: "event", name: "change", identifier: 3, description: "Notifies that the some state of the service changed.", fields: [] }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "StatusCodes", storage: 2 }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16 u16" }, { kind: "event", name: "neutral", identifier: 7, description: "Notifies that the threshold is back between `low` and `high`.", fields: [] }], tags: [] }, { name: "Base service", status: "stable", shortId: "_base", camelName: "base", shortName: "base", extends: [], notes: { short: "Base class for all services." }, classIdentifier: 536870899, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16" }], tags: [] }, { name: "Sensor", status: "stable", shortId: "_sensor", camelName: "sensor", shortName: "sensor", extends: ["_base"], notes: { short: "Base class for sensors." }, classIdentifier: 536870898, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32" }], tags: [] }, { name: "Accelerometer", status: "stable", shortId: "accelerometer", camelName: "accelerometer", shortName: "accelerometer", extends: ["_base", "_sensor"], notes: { short: "A 3-axis accelerometer.", long: "## Orientation\n\nAn accelerometer module should translate acceleration values as follows:\n\n| Orientation | X value (g) | Y value (g) | Z value (g) |\n|----------------------- |------------- |------------- |------------- |\n| Module lying flat | 0 | 0 | -1 |\n| Module on left edge | -1 | 0 | 0 |\n| Module on bottom edge | 0 | 1 | 0 |\n\nWe recommend an orientation marking on the PCB so that users can mount modules without having to experiment with the device. Left/bottom can be determined by assuming text on silk runs left-to-right.", events: "All events are debounced." }, classIdentifier: 521405449, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "forces", identifier: 257, description: "Indicates the current forces acting on accelerometer.", fields: [{ name: "x", unit: "g", shift: 20, type: "i12.20", storage: -4 }, { name: "y", unit: "g", shift: 20, type: "i12.20", storage: -4 }, { name: "z", unit: "g", shift: 20, type: "i12.20", storage: -4 }], volatile: true, identifierName: "reading", packFormat: "i12.20 i12.20 i12.20" }, { kind: "ro", name: "forces_error", identifier: 262, description: "Error on the reading value.", fields: [{ name: "_", unit: "g", shift: 20, type: "u12.20", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u12.20" }, { kind: "rw", name: "max_force", identifier: 8, description: 'Configures the range forces detected.\nThe value will be "rounded up" to one of `max_forces_supported`.', fields: [{ name: "_", unit: "g", shift: 20, type: "u12.20", storage: 4 }], optional: true, identifierName: "reading_range", packFormat: "u12.20" }, { kind: "const", name: "max_forces_supported", identifier: 266, description: "Lists values supported for writing `max_force`.", fields: [{ name: "max_force", unit: "g", shift: 20, type: "u12.20", storage: 4, startRepeats: true }], optional: true, identifierName: "supported_ranges", packFormat: "r: u12.20" }, { kind: "event", name: "tilt_up", identifier: 129, description: "Emitted when accelerometer is tilted in the given direction.", fields: [] }, { kind: "event", name: "tilt_down", identifier: 130, description: "Emitted when accelerometer is tilted in the given direction.", fields: [] }, { kind: "event", name: "tilt_left", identifier: 131, description: "Emitted when accelerometer is tilted in the given direction.", fields: [] }, { kind: "event", name: "tilt_right", identifier: 132, description: "Emitted when accelerometer is tilted in the given direction.", fields: [] }, { kind: "event", name: "face_up", identifier: 133, description: "Emitted when accelerometer is laying flat in the given direction.", fields: [] }, { kind: "event", name: "face_down", identifier: 134, description: "Emitted when accelerometer is laying flat in the given direction.", fields: [] }, { kind: "event", name: "freefall", identifier: 135, description: "Emitted when total force acting on accelerometer is much less than 1g.", fields: [] }, { kind: "event", name: "shake", identifier: 139, description: "Emitted when forces change violently a few times.", fields: [] }, { kind: "event", name: "force_2g", identifier: 140, description: "Emitted when force in any direction exceeds given threshold.", fields: [] }, { kind: "event", name: "force_3g", identifier: 136, description: "Emitted when force in any direction exceeds given threshold.", fields: [] }, { kind: "event", name: "force_6g", identifier: 137, description: "Emitted when force in any direction exceeds given threshold.", fields: [] }, { kind: "event", name: "force_8g", identifier: 138, description: "Emitted when force in any direction exceeds given threshold.", fields: [] }], tags: ["C"], group: "Movement" }, { name: "Acidity", status: "experimental", shortId: "acidity", camelName: "acidity", shortName: "acidity", extends: ["_base", "_sensor"], notes: { short: "A sensor measuring water acidity, commonly called pH." }, classIdentifier: 513243333, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "acidity", identifier: 257, description: "The acidity, pH, of water.", fields: [{ name: "_", unit: "pH", shift: 12, type: "u4.12", storage: 2, absoluteMin: 0, absoluteMax: 15, typicalMin: 2.5, typicalMax: 10.5 }], volatile: true, identifierName: "reading", preferredInterval: 5e3, packFormat: "u4.12" }, { kind: "ro", name: "acidity_error", identifier: 262, description: "Error on the acidity reading.", fields: [{ name: "_", unit: "pH", shift: 12, type: "u4.12", storage: 2 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u4.12" }, { kind: "const", name: "min_acidity", identifier: 260, description: "Lowest acidity that can be reported.", fields: [{ name: "_", unit: "pH", shift: 12, type: "u4.12", storage: 2 }], optional: true, identifierName: "min_reading", packFormat: "u4.12" }, { kind: "const", name: "max_humidity", identifier: 261, description: "Highest acidity that can be reported.", fields: [{ name: "_", unit: "pH", shift: 12, type: "u4.12", storage: 2 }], optional: true, identifierName: "max_reading", packFormat: "u4.12" }], tags: ["C", "8bit"], group: "Environment" }, { name: "Air Pressure", status: "rc", shortId: "airpressure", camelName: "airPressure", shortName: "airPressure", extends: ["_base", "_sensor"], notes: { short: "A sensor measuring air pressure of outside environment.", registers: "Default streaming interval is 1s." }, classIdentifier: 504462570, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "pressure", identifier: 257, description: "The air pressure.", fields: [{ name: "_", unit: "hPa", shift: 10, type: "u22.10", storage: 4, typicalMin: 150, typicalMax: 1150 }], volatile: true, identifierName: "reading", preferredInterval: 1e3, packFormat: "u22.10" }, { kind: "ro", name: "pressure_error", identifier: 262, description: "The real pressure is between `pressure - pressure_error` and `pressure + pressure_error`.", fields: [{ name: "_", unit: "hPa", shift: 10, type: "u22.10", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u22.10" }, { kind: "const", name: "min_pressure", identifier: 260, description: "Lowest air pressure that can be reported.", fields: [{ name: "_", unit: "hPa", shift: 10, type: "u22.10", storage: 4 }], optional: true, identifierName: "min_reading", packFormat: "u22.10" }, { kind: "const", name: "max_pressure", identifier: 261, description: "Highest air pressure that can be reported.", fields: [{ name: "_", unit: "hPa", shift: 10, type: "u22.10", storage: 4 }], optional: true, identifierName: "max_reading", packFormat: "u22.10" }], tags: ["8bit"], group: "Environment" }, { name: "Air Quality Index", status: "experimental", shortId: "airqualityindex", camelName: "airQualityIndex", shortName: "airQualityIndex", extends: ["_base", "_sensor"], notes: { short: "The Air Quality Index is a measure of how clean or polluted air is. From min, good quality, to high, low quality.\nThe range of AQI may vary between countries (https://en.wikipedia.org/wiki/Air_quality_index)." }, classIdentifier: 346844886, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "aqi_index", identifier: 257, description: "Air quality index, typically refreshed every second.", fields: [{ name: "_", unit: "AQI", shift: 16, type: "u16.16", storage: 4, typicalMax: 500, typicalMin: 0 }], volatile: true, identifierName: "reading", preferredInterval: 6e4, packFormat: "u16.16" }, { kind: "ro", name: "aqi_index_error", identifier: 262, description: "Error on the AQI measure.", fields: [{ name: "_", unit: "AQI", shift: 16, type: "u16.16", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u16.16" }, { kind: "const", name: "min_aqi_index", identifier: 260, description: "Minimum AQI reading, representing a good air quality. Typically 0.", fields: [{ name: "_", unit: "AQI", shift: 16, type: "u16.16", storage: 4 }], identifierName: "min_reading", packFormat: "u16.16" }, { kind: "const", name: "max_aqi_index", identifier: 261, description: "Maximum AQI reading, representing a very poor air quality.", fields: [{ name: "_", unit: "AQI", shift: 16, type: "u16.16", storage: 4 }], identifierName: "max_reading", packFormat: "u16.16" }], tags: [], group: "Environment" }, { name: "Arcade Gamepad", status: "deprecated", shortId: "arcadegamepad", camelName: "arcadeGamepad", shortName: "arcadeGamepad", extends: ["_base", "_sensor"], notes: { short: "This service is deprecated in favor of `gamepad` (although it is currently used by the micro:bit Arcade smart shield).\nA gamepad with direction and action buttons for one player.\nIf a device has multiple controllers, it should have multiple gamepad services, using consecutive service identifiers." }, classIdentifier: 501915758, enums: { Button: { name: "Button", storage: 1, members: { Left: 1, Up: 2, Right: 3, Down: 4, A: 5, B: 6, Menu: 7, Select: 8, Reset: 9, Exit: 10 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "buttons", identifier: 257, description: "Indicates which buttons are currently active (pressed).\n`pressure` should be `0xff` for digital buttons, and proportional for analog ones.", fields: [{ name: "button", type: "Button", storage: 1, startRepeats: true }, { name: "pressure", unit: "/", shift: 8, type: "u0.8", storage: 1 }], volatile: true, identifierName: "reading", packFormat: "r: u8 u0.8" }, { kind: "const", name: "available_buttons", identifier: 384, description: "Indicates number of players supported and which buttons are present on the controller.", fields: [{ name: "button", type: "Button", storage: 1, startRepeats: true }], packFormat: "r: u8" }, { kind: "event", name: "down", identifier: 1, description: "Emitted when button goes from inactive to active.", fields: [{ name: "button", type: "Button", storage: 1 }], identifierName: "active", packFormat: "u8" }, { kind: "event", name: "up", identifier: 2, description: "Emitted when button goes from active to inactive.", fields: [{ name: "button", type: "Button", storage: 1 }], identifierName: "inactive", packFormat: "u8" }], tags: [], group: "Button" }, { name: "Arcade Sound", status: "experimental", shortId: "arcadesound", camelName: "arcadeSound", shortName: "arcadeSound", extends: ["_base"], notes: { short: "A sound playing device.\n\nThis is typically run over an SPI connection, not regular single-wire Jacdac." }, classIdentifier: 533083654, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "play", identifier: 128, description: "Play samples, which are single channel, signed 16-bit little endian values.", fields: [{ name: "samples", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "b" }, { kind: "rw", name: "sample_rate", identifier: 128, description: "Get or set playback sample rate (in samples per second).\nIf you set it, read it back, as the value may be rounded up or down.", fields: [{ name: "_", unit: "Hz", shift: 10, type: "u22.10", storage: 4, defaultValue: 44100 }], packFormat: "u22.10" }, { kind: "const", name: "buffer_size", identifier: 384, description: "The size of the internal audio buffer.", fields: [{ name: "_", unit: "B", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "ro", name: "buffer_pending", identifier: 385, description: "How much data is still left in the buffer to play.\nClients should not send more data than `buffer_size - buffer_pending`,\nbut can keep the `buffer_pending` as low as they want to ensure low latency\nof audio playback.", fields: [{ name: "_", unit: "B", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }], tags: ["SPI"] }, { name: "Barcode reader", status: "experimental", shortId: "barcodereader", camelName: "barcodeReader", shortName: "barcodeReader", extends: ["_base"], notes: { short: "A device that reads various barcodes, like QR codes. For the web, see [BarcodeDetector](https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector)." }, classIdentifier: 477339244, enums: { Format: { name: "Format", storage: 1, members: { Aztec: 1, Code128: 2, Code39: 3, Code93: 4, Codabar: 5, DataMatrix: 6, Ean13: 8, Ean8: 9, ITF: 10, Pdf417: 11, QrCode: 12, UpcA: 13, UpcE: 14 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "enabled", identifier: 1, description: "Turns on or off the detection of barcodes.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "const", name: "formats", identifier: 384, description: "Reports the list of supported barcode formats, as documented in https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API.", fields: [{ name: "format", type: "Format", storage: 1, startRepeats: true }], optional: true, packFormat: "r: u8" }, { kind: "event", name: "detect", identifier: 1, description: "Raised when a bar code is detected and decoded. If the reader detects multiple codes, it will issue multiple events.\nIn case of numeric barcodes, the `data` field should contain the ASCII (which is the same as UTF8 in that case) representation of the number.", fields: [{ name: "format", type: "Format", storage: 1 }, { name: "data", type: "string", storage: 0 }], identifierName: "active", packFormat: "u8 s" }], tags: [] }, { name: "bit:radio", status: "stable", shortId: "bitradio", camelName: "bitRadio", shortName: "bitRadio", extends: ["_base"], notes: { short: "Support for sending and receiving packets using the [Bit Radio protocol](https://github.com/microsoft/pxt-common-packages/blob/master/libs/radio/docs/reference/radio.md), typically used between micro:bit devices." }, classIdentifier: 449414863, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "enabled", identifier: 1, description: "Turns on/off the radio antenna.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "rw", name: "group", identifier: 128, description: "Group used to filter packets", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "rw", name: "transmission_power", identifier: 129, description: "Antenna power to increase or decrease range.", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true, defaultValue: 6, absoluteMin: 1, absoluteMax: 7 }], packFormat: "u8" }, { kind: "rw", name: "frequency_band", identifier: 130, description: "Change the transmission and reception band of the radio to the given channel.", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true, defaultValue: 7, absoluteMax: 83, absoluteMin: 0 }], packFormat: "u8" }, { kind: "command", name: "send_string", identifier: 128, description: "Sends a string payload as a radio message, maximum 18 characters.", fields: [{ name: "message", type: "string", storage: 0 }], unique: true, packFormat: "s" }, { kind: "command", name: "send_number", identifier: 129, description: "Sends a double precision number payload as a radio message", fields: [{ name: "value", isFloat: true, type: "f64", storage: 8 }], unique: true, packFormat: "f64" }, { kind: "command", name: "send_value", identifier: 130, description: "Sends a double precision number and a name payload as a radio message", fields: [{ name: "value", isFloat: true, type: "f64", storage: 8 }, { name: "name", type: "string", storage: 0 }], unique: true, packFormat: "f64 s" }, { kind: "command", name: "send_buffer", identifier: 131, description: "Sends a payload of bytes as a radio message", fields: [{ name: "data", type: "bytes", storage: 0, isSimpleType: true }], unique: true, packFormat: "b" }, { kind: "report", name: "string_received", identifier: 144, description: "Raised when a string packet is received", fields: [{ name: "time", unit: "ms", type: "u32", storage: 4, isSimpleType: true }, { name: "device_serial_number", type: "u32", storage: 4, isSimpleType: true }, { name: "rssi", unit: "dB", type: "i8", storage: -1, isSimpleType: true }, { name: "padding", type: "u8[1]", storage: 1 }, { name: "message", type: "string", storage: 0 }], packFormat: "u32 u32 i8 b[1] s" }, { kind: "report", name: "number_received", identifier: 145, description: "Raised when a number packet is received", fields: [{ name: "time", unit: "ms", type: "u32", storage: 4, isSimpleType: true }, { name: "device_serial_number", type: "u32", storage: 4, isSimpleType: true }, { name: "rssi", unit: "dB", type: "i8", storage: -1, isSimpleType: true }, { name: "padding", type: "u8[3]", storage: 3 }, { name: "value", isFloat: true, type: "f64", storage: 8 }, { name: "name", type: "string", storage: 0 }], packed: true, packFormat: "u32 u32 i8 b[3] f64 s" }, { kind: "report", name: "buffer_received", identifier: 146, description: "Raised when a buffer packet is received", fields: [{ name: "time", unit: "ms", type: "u32", storage: 4, isSimpleType: true }, { name: "device_serial_number", type: "u32", storage: 4, isSimpleType: true }, { name: "rssi", unit: "dB", type: "i8", storage: -1, isSimpleType: true }, { name: "padding", type: "u8[1]", storage: 1 }, { name: "data", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "u32 u32 i8 b[1] b" }], tags: [] }, { name: "Bootloader", status: "stable", shortId: "bootloader", camelName: "bootloader", shortName: "bootloader", extends: ["_base"], notes: { short: "Allows flashing (reprogramming) devices over Jacdac.\n\nThis is typically implemented by having a separate _bootloader_ mode, as opposed to _application_ mode\non the module to be flashed.\nThe bootloader mode is entered on every device reset, for about 300ms.\nThe bootloader will generally not announce itself, until it gets some command.\nOnce it gets the command, it will stay in application mode.\n\nTypically, you ask the module (in application mode) to reset, while sending broadcast\n`info` commands to all bootloaders every 100ms or so.\nAlternatively, you ask the the user to disconnect and re-connect the module, while\nbroadcasting `info` commands.\nThe second method works even if the application is damaged (eg., due to an aborted flash).\n\nWhen device is in bootloader mode, the device ID may change compared to application mode,\nby flipping the first bit in the first byte of the device identifier." }, classIdentifier: 536516936, enums: { Error: { name: "Error", storage: 4, members: { NoError: 0, PacketTooSmall: 1, OutOfFlashableRange: 2, InvalidPageOffset: 3, NotPageAligned: 4 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "info", identifier: 0, description: 'The `service_class` is always `0x1ffa9948`. The `product_identifier` identifies the kind of firmware\nthat "fits" this device.', fields: [], identifierName: "announce", hasReport: true }, { kind: "report", name: "info", identifier: 0, description: 'The `service_class` is always `0x1ffa9948`. The `product_identifier` identifies the kind of firmware\nthat "fits" this device.', fields: [{ name: "service_class", type: "u32", storage: 4, isSimpleType: true }, { name: "page_size", unit: "B", type: "u32", storage: 4, isSimpleType: true }, { name: "flashable_size", unit: "B", type: "u32", storage: 4, isSimpleType: true }, { name: "product_identifier", type: "u32", storage: 4, isSimpleType: true }], secondary: true, packFormat: "u32 u32 u32 u32" }, { kind: "command", name: "set_session", identifier: 129, description: "The flashing server should generate a random id, and use this command to set it.", fields: [{ name: "session_id", type: "u32", storage: 4, isSimpleType: true }], hasReport: true, packFormat: "u32" }, { kind: "report", name: "set_session", identifier: 129, description: "The flashing server should generate a random id, and use this command to set it.", fields: [{ name: "session_id", type: "u32", storage: 4, isSimpleType: true }], secondary: true, packFormat: "u32" }, { kind: "command", name: "page_data", identifier: 128, description: "Use to send flashing data. A physical page is split into `chunk_max + 1` chunks, where `chunk_no = 0 ... chunk_max`.\nEach chunk is stored at `page_address + page_offset`. `page_address` has to be equal in all chunks,\nand is included in response.\nOnly the last chunk causes writing to flash and elicits response.\n\nErrors not listed are also possible. Errors larger than `0xffff` indicate de-synchronization on chunk numbers.\n\nWhile this command is technically `unique`, the bootloader client will retry failed pages.\nBootloaders typically will not support reliable commands delivered over pipes.", fields: [{ name: "page_address", type: "u32", storage: 4, isSimpleType: true }, { name: "page_offset", type: "u16", storage: 2, isSimpleType: true }, { name: "chunk_no", type: "u8", storage: 1, isSimpleType: true }, { name: "chunk_max", type: "u8", storage: 1, isSimpleType: true }, { name: "session_id", type: "u32", storage: 4, isSimpleType: true }, { name: "reserved0", type: "u32", storage: 4, isSimpleType: true }, { name: "reserved1", type: "u32", storage: 4, isSimpleType: true }, { name: "reserved2", type: "u32", storage: 4, isSimpleType: true }, { name: "reserved3", type: "u32", storage: 4, isSimpleType: true }, { name: "page_data", type: "bytes", storage: 208, isSimpleType: true, maxBytes: 208 }], unique: true, hasReport: true, packFormat: "u32 u16 u8 u8 u32 u32 u32 u32 u32 b[208]" }, { kind: "report", name: "page_data", identifier: 128, description: "Use to send flashing data. A physical page is split into `chunk_max + 1` chunks, where `chunk_no = 0 ... chunk_max`.\nEach chunk is stored at `page_address + page_offset`. `page_address` has to be equal in all chunks,\nand is included in response.\nOnly the last chunk causes writing to flash and elicits response.\n\nErrors not listed are also possible. Errors larger than `0xffff` indicate de-synchronization on chunk numbers.\n\nWhile this command is technically `unique`, the bootloader client will retry failed pages.\nBootloaders typically will not support reliable commands delivered over pipes.", fields: [{ name: "session_id", type: "u32", storage: 4, isSimpleType: true }, { name: "page_error", type: "Error", storage: 4 }, { name: "page_address", type: "u32", storage: 4, isSimpleType: true }], secondary: true, packFormat: "u32 u32 u32" }], tags: ["C", "management"] }, { name: "Braille display", status: "stable", shortId: "brailledisplay", camelName: "brailleDisplay", shortName: "brailleDisplay", extends: ["_base"], notes: { short: "A Braille pattern display module. This module display [unicode braille patterns](https://www.unicode.org/charts/PDF/U2800.pdf), country specific encoding have to be implemented by the clients." }, classIdentifier: 331331532, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "enabled", identifier: 1, description: "Determines if the braille display is active.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "rw", name: "patterns", identifier: 2, description: "Braille patterns to show. Must be unicode characters between `0x2800` and `0x28ff`.", fields: [{ name: "_", type: "string", storage: 0 }], lowLevel: true, identifierName: "value", packFormat: "s" }, { kind: "const", name: "length", identifier: 385, description: "Gets the number of patterns that can be displayed.", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }], tags: [], group: "Display" }, { name: "Bridge", status: "rc", shortId: "bridge", camelName: "bridge", shortName: "bridge", extends: ["_base"], notes: { short: "Indicates that the device acts as a bridge to the Jacdac bus." }, classIdentifier: 535147631, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "enabled", identifier: 1, description: "Enables or disables the bridge.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }], tags: ["infrastructure"] }, { name: "Button", status: "stable", shortId: "button", camelName: "button", shortName: "button", extends: ["_base", "_sensor"], notes: { short: "A push-button, which returns to inactive position when not operated anymore." }, classIdentifier: 343122531, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "pressure", identifier: 257, description: "Indicates the pressure state of the button, where `0` is open.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, identifierName: "reading", packFormat: "u0.16" }, { kind: "const", name: "analog", identifier: 384, description: "Indicates if the button provides analog `pressure` readings.", fields: [{ name: "_", type: "bool", storage: 1 }], optional: true, packFormat: "u8" }, { kind: "ro", name: "pressed", identifier: 385, description: "Determines if the button is pressed currently.\n\nIf the event `down` or `hold` is observed, `pressed` becomes true; if `up` is observed, `pressed` becomes false.\nThe client should initialize `pressed` to false.", fields: [{ name: "_", type: "bool", storage: 1 }], client: true, volatile: true, packFormat: "u8" }, { kind: "event", name: "down", identifier: 1, description: "Emitted when button goes from inactive to active.", fields: [], identifierName: "active" }, { kind: "event", name: "up", identifier: 2, description: "Emitted when button goes from active to inactive. The 'time' parameter\nrecords the amount of time between the down and up events.", fields: [{ name: "time", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], identifierName: "inactive", packFormat: "u32" }, { kind: "event", name: "hold", identifier: 129, description: "Emitted when the press time is greater than 500ms, and then at least every 500ms\nas long as the button remains pressed. The 'time' parameter records the the amount of time\nthat the button has been held (since the down event).", fields: [{ name: "time", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }], tags: ["C", "8bit", "padauk", "input"], group: "Button" }, { name: "Buzzer", status: "rc", shortId: "buzzer", camelName: "buzzer", shortName: "buzzer", extends: ["_base"], notes: { short: "A simple buzzer." }, classIdentifier: 458731991, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "volume", identifier: 1, description: "The volume (duty cycle) of the buzzer.", fields: [{ name: "_", unit: "/", shift: 8, type: "u0.8", storage: 1, defaultValue: 1 }], identifierName: "intensity", packFormat: "u0.8" }, { kind: "command", name: "play_tone", identifier: 128, description: "Play a PWM tone with given period and duty for given duration.\nThe duty is scaled down with `volume` register.\nTo play tone at frequency `F` Hz and volume `V` (in `0..1`) you will want\nto send `P = 1000000 / F` and `D = P * V / 2`.", fields: [{ name: "period", unit: "us", type: "u16", storage: 2, isSimpleType: true }, { name: "duty", unit: "us", type: "u16", storage: 2, isSimpleType: true }, { name: "duration", unit: "ms", type: "u16", storage: 2, isSimpleType: true }], lowLevel: true, packFormat: "u16 u16 u16" }, { kind: "command", name: "play_note", identifier: 129, description: "Play a note at the given frequency and volume.", fields: [{ name: "frequency", unit: "AudHz", type: "u16", storage: 2, isSimpleType: true }, { name: "volume", unit: "/", shift: 16, type: "u0.16", storage: 2 }, { name: "duration", unit: "ms", type: "u16", storage: 2, isSimpleType: true }], client: true, packFormat: "u16 u0.16 u16" }], tags: ["C", "8bit"], group: "Sound" }, { name: "Capacitive Button", status: "rc", shortId: "capacitivebutton", camelName: "capacitiveButton", shortName: "capacitiveButton", extends: ["_base"], notes: { short: "A configuration service for a capacitive push-button." }, classIdentifier: 677752265, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "threshold", identifier: 6, description: "Indicates the threshold for ``up`` events.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], identifierName: "active_threshold", packFormat: "u0.16" }, { kind: "command", name: "calibrate", identifier: 2, description: "Request to calibrate the capactive. When calibration is requested, the device expects that no object is touching the button. \nThe report indicates the calibration is done.", fields: [], identifierName: "calibrate", hasReport: true }, { kind: "report", name: "calibrate", identifier: 2, description: "Request to calibrate the capactive. When calibration is requested, the device expects that no object is touching the button. \nThe report indicates the calibration is done.", fields: [], secondary: true }], tags: ["8bit"], group: "Button" }, { name: "Character Screen", status: "rc", shortId: "characterscreen", camelName: "characterScreen", shortName: "characterScreen", extends: ["_base"], notes: { short: "A screen that displays characters, typically a LCD/OLED character screen." }, classIdentifier: 523748714, enums: { Variant: { name: "Variant", storage: 1, members: { LCD: 1, OLED: 2, Braille: 3 } }, TextDirection: { name: "TextDirection", storage: 1, members: { LeftToRight: 1, RightToLeft: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "message", identifier: 2, description: "Text to show. Use `\\n` to break lines.", fields: [{ name: "_", type: "string", storage: 0 }], identifierName: "value", packFormat: "s" }, { kind: "rw", name: "brightness", identifier: 1, description: "Brightness of the screen. `0` means off.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], optional: true, identifierName: "intensity", packFormat: "u0.16" }, { kind: "const", name: "variant", identifier: 263, description: "Describes the type of character LED screen.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }, { kind: "rw", name: "text_direction", identifier: 130, description: "Specifies the RTL or LTR direction of the text.", fields: [{ name: "_", type: "TextDirection", storage: 1 }], optional: true, packFormat: "u8" }, { kind: "const", name: "rows", identifier: 384, description: "Gets the number of rows.", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "const", name: "columns", identifier: 385, description: "Gets the number of columns.", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }], tags: [], group: "Display" }, { name: "Cloud Adapter", status: "experimental", shortId: "cloudadapter", camelName: "cloudAdapter", shortName: "cloudAdapter", extends: ["_base"], notes: { short: "Supports cloud connections to upload and download data.\nNote that `f64` values following a label are not necessarily aligned." }, classIdentifier: 341864092, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "upload_json", identifier: 128, description: "Upload a JSON-encoded message to the cloud.", fields: [{ name: "topic", type: "string0", storage: 0 }, { name: "json", type: "string", storage: 0 }], packFormat: "z s" }, { kind: "command", name: "upload_binary", identifier: 129, description: "Upload a binary message to the cloud.", fields: [{ name: "topic", type: "string0", storage: 0 }, { name: "payload", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "z b" }, { kind: "ro", name: "connected", identifier: 384, description: "Indicate whether we're currently connected to the cloud server.\nWhen offline, `upload` commands are queued.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "ro", name: "connection_name", identifier: 385, description: 'User-friendly name of the connection, typically includes name of the server\nand/or type of cloud service (`"something.cloud.net (Provider IoT)"`).', fields: [{ name: "_", type: "string", storage: 0 }], packFormat: "s" }, { kind: "event", name: "on_json", identifier: 128, description: "Emitted when cloud send us a JSON message.", fields: [{ name: "topic", type: "string0", storage: 0 }, { name: "json", type: "string", storage: 0 }], packFormat: "z s" }, { kind: "event", name: "on_binary", identifier: 129, description: "Emitted when cloud send us a binary message.", fields: [{ name: "topic", type: "string0", storage: 0 }, { name: "payload", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "z b" }, { kind: "event", name: "change", identifier: 3, description: "Emitted when we connect or disconnect from the cloud.", fields: [], identifierName: "change" }], tags: ["infrastructure", "devicescript"], group: "Iot", restricted: true }, { name: "Cloud Configuration", status: "rc", shortId: "cloudconfiguration", camelName: "cloudConfiguration", shortName: "cloudConfiguration", extends: ["_base"], notes: { short: "Connection and diagnostics information about the cloud connection." }, classIdentifier: 342028028, enums: { ConnectionStatus: { name: "ConnectionStatus", storage: 2, members: { Connected: 1, Disconnected: 2, Connecting: 3, Disconnecting: 4 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "ro", name: "server_name", identifier: 384, description: "Something like `my-iot-hub.azure-devices.net` if available.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, packFormat: "s" }, { kind: "ro", name: "cloud_device_id", identifier: 385, description: "Device identifier for the device in the cloud if available.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, packFormat: "s" }, { kind: "const", name: "cloud_type", identifier: 387, description: "Cloud provider identifier.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, packFormat: "s" }, { kind: "ro", name: "connection_status", identifier: 386, description: "Indicates the status of connection. A message beyond the [0..3] range represents an HTTP error code.", fields: [{ name: "_", type: "ConnectionStatus", storage: 2 }], packFormat: "u16" }, { kind: "rw", name: "push_period", identifier: 128, description: "How often to push data to the cloud.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 5e3 }], packFormat: "u32" }, { kind: "rw", name: "push_watchdog_period", identifier: 129, description: "If no message is published within given period, the device resets.\nThis can be due to connectivity problems or due to the device having nothing to publish.\nForced to be at least `2 * flush_period`.\nSet to `0` to disable (default).", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "command", name: "connect", identifier: 129, description: "Starts a connection to the cloud service", fields: [], restricted: true }, { kind: "command", name: "disconnect", identifier: 130, description: "Starts disconnecting from the cloud service", fields: [], restricted: true }, { kind: "command", name: "set_connection_string", identifier: 134, description: "Restricted command to override the existing connection string to cloud.", fields: [{ name: "connection_string", type: "string", storage: 0 }], restricted: true, packFormat: "s" }, { kind: "event", name: "connection_status_change", identifier: 3, description: "Raised when the connection status changes", fields: [{ name: "connection_status", type: "ConnectionStatus", storage: 2 }], identifierName: "change", packFormat: "u16" }, { kind: "event", name: "message_sent", identifier: 128, description: "Raised when a message has been sent to the hub.", fields: [] }], tags: ["management"], group: "Iot" }, { name: "CODAL Message Bus", status: "experimental", shortId: "codalmessagebus", camelName: "codalMessageBus", shortName: "codalMessageBus", extends: ["_base"], notes: { short: "A service that uses the [CODAL message bus](https://lancaster-university.github.io/microbit-docs/ubit/messageBus/) to send and receive small messages.\n\nYou can find known values for `source` in [CODAL repository](https://github.com/lancaster-university/codal-core/blob/master/inc/core/CodalComponent.h)\nIn MakeCode, you can listen for custom `source`, `value` values using [control.onEvent](https://makecode.microbit.org/reference/control/on-event]." }, classIdentifier: 304085021, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "send", identifier: 128, description: "Send a message on the CODAL bus. If `source` is `0`, it is treated as wildcard.", fields: [{ name: "source", type: "u16", storage: 2, isSimpleType: true }, { name: "value", type: "u16", storage: 2, isSimpleType: true }], unique: true, packFormat: "u16 u16" }, { kind: "event", name: "message", identifier: 128, description: "Raised by the server is triggered by the server. The filtering logic of which event to send over Jacdac is up to the server implementation.", fields: [{ name: "source", type: "u16", storage: 2, isSimpleType: true }, { name: "value", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16 u16" }], tags: [] }, { name: "Color", status: "experimental", shortId: "color", camelName: "color", shortName: "color", extends: ["_base", "_sensor"], notes: { short: "Senses RGB colors" }, classIdentifier: 372299111, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "color", identifier: 257, description: "Detected color in the RGB color space.", fields: [{ name: "red", unit: "/", shift: 16, type: "u0.16", storage: 2 }, { name: "green", unit: "/", shift: 16, type: "u0.16", storage: 2 }, { name: "blue", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, identifierName: "reading", packFormat: "u0.16 u0.16 u0.16" }], tags: ["8bit"], group: "Environment" }, { name: "Compass", status: "rc", shortId: "compass", camelName: "compass", shortName: "compass", extends: ["_base", "_sensor"], notes: { short: "A sensor that measures the heading." }, classIdentifier: 364362175, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "heading", identifier: 257, description: "The heading with respect to the magnetic north.", fields: [{ name: "_", unit: "\xB0", shift: 16, type: "u16.16", storage: 4, absoluteMin: 0, absoluteMax: 359 }], volatile: true, identifierName: "reading", preferredInterval: 1e3, packFormat: "u16.16" }, { kind: "rw", name: "enabled", identifier: 1, description: "Turn on or off the sensor. Turning on the sensor may start a calibration sequence.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "ro", name: "heading_error", identifier: 262, description: "Error on the heading reading", fields: [{ name: "_", unit: "\xB0", shift: 16, type: "u16.16", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u16.16" }, { kind: "command", name: "calibrate", identifier: 2, description: "Starts a calibration sequence for the compass.", fields: [], identifierName: "calibrate" }], tags: [] }, { name: "Control", status: "stable", shortId: "control", camelName: "control", shortName: "control", extends: ["_base"], notes: { short: "Control service is always service index `0`.\nIt handles actions common to all services on a device.\n\nNote: some of the optional features (including `flood_ping`, `mcu_temperature`, and all string registers)\nare not implemented in `8bit` version." }, classIdentifier: 0, enums: { AnnounceFlags: { name: "AnnounceFlags", storage: 2, isFlags: true, members: { RestartCounterSteady: 15, RestartCounter1: 1, RestartCounter2: 2, RestartCounter4: 4, RestartCounter8: 8, StatusLightNone: 0, StatusLightMono: 16, StatusLightRgbNoFade: 32, StatusLightRgbFade: 48, SupportsACK: 256, SupportsBroadcast: 512, SupportsFrames: 1024, IsClient: 2048, SupportsReliableCommands: 4096 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "services", identifier: 0, description: "The `restart_counter` is computed from the `flags & RestartCounterSteady`, starts at `0x1` and increments by one until it reaches `0xf`, then it stays at `0xf`.\nIf this number ever goes down, it indicates that the device restarted.\n`service_class` indicates class identifier for each service index (service index `0` is always control, so it's\nskipped in this enumeration).\n`packet_count` indicates the number of reports sent by the current device since last announce,\nincluding the current announce packet (it is always 0 if this feature is not supported).\nThe command form can be used to induce report, which is otherwise broadcast every 500ms.", fields: [], identifierName: "announce", hasReport: true }, { kind: "report", name: "services", identifier: 0, description: "The `restart_counter` is computed from the `flags & RestartCounterSteady`, starts at `0x1` and increments by one until it reaches `0xf`, then it stays at `0xf`.\nIf this number ever goes down, it indicates that the device restarted.\n`service_class` indicates class identifier for each service index (service index `0` is always control, so it's\nskipped in this enumeration).\n`packet_count` indicates the number of reports sent by the current device since last announce,\nincluding the current announce packet (it is always 0 if this feature is not supported).\nThe command form can be used to induce report, which is otherwise broadcast every 500ms.", fields: [{ name: "flags", type: "AnnounceFlags", storage: 2 }, { name: "packet_count", type: "u8", storage: 1, isSimpleType: true }, { name: "reserved", type: "u8", storage: 1, isSimpleType: true }, { name: "service_class", type: "u32", storage: 4, isSimpleType: true, startRepeats: true }], secondary: true, packFormat: "u16 u8 u8 r: u32" }, { kind: "command", name: "noop", identifier: 128, description: "Do nothing. Always ignored. Can be used to test ACKs.", fields: [] }, { kind: "command", name: "identify", identifier: 129, description: "Blink the status LED (262ms on, 262ms off, four times, with the blue LED) or otherwise draw user's attention to device with no status light.\nFor devices with status light (this can be discovered in the announce flags), the client should\nsend the sequence of status light command to generate the identify animation.", fields: [], optional: true }, { kind: "command", name: "reset", identifier: 130, description: "Reset device. ACK may or may not be sent.", fields: [], optional: true }, { kind: "command", name: "flood_ping", identifier: 131, description: "The device will respond `num_responses` times, as fast as it can, setting the `counter` field in the report\nto `start_counter`, then `start_counter + 1`, ..., and finally `start_counter + num_responses - 1`.\nThe `dummy_payload` is `size` bytes long and contains bytes `0, 1, 2, ...`.", fields: [{ name: "num_responses", type: "u32", storage: 4, isSimpleType: true }, { name: "start_counter", type: "u32", storage: 4, isSimpleType: true }, { name: "size", unit: "B", type: "u8", storage: 1, isSimpleType: true }], unique: true, optional: true, hasReport: true, packFormat: "u32 u32 u8" }, { kind: "report", name: "flood_ping", identifier: 131, description: "The device will respond `num_responses` times, as fast as it can, setting the `counter` field in the report\nto `start_counter`, then `start_counter + 1`, ..., and finally `start_counter + num_responses - 1`.\nThe `dummy_payload` is `size` bytes long and contains bytes `0, 1, 2, ...`.", fields: [{ name: "counter", type: "u32", storage: 4, isSimpleType: true }, { name: "dummy_payload", type: "bytes", storage: 0, isSimpleType: true }], secondary: true, packFormat: "u32 b" }, { kind: "command", name: "set_status_light", identifier: 132, description: "Initiates a color transition of the status light from its current color to the one specified.\nThe transition will complete in about `512 / speed` frames\n(each frame is currently 100ms, so speed of `51` is about 1 second and `26` 0.5 second).\nAs a special case, if speed is `0` the transition is immediate.\nIf MCU is not capable of executing transitions, it can consider `speed` to be always `0`.\nIf a monochrome LEDs is fitted, the average value of `red`, `green`, `blue` is used.\nIf intensity of a monochrome LED cannot be controlled, any value larger than `0` should be considered\non, and `0` (for all three channels) should be considered off.", fields: [{ name: "to_red", type: "u8", storage: 1, isSimpleType: true }, { name: "to_green", type: "u8", storage: 1, isSimpleType: true }, { name: "to_blue", type: "u8", storage: 1, isSimpleType: true }, { name: "speed", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8 u8 u8 u8" }, { kind: "command", name: "proxy", identifier: 133, description: "Force client device into proxy mode.", fields: [], optional: true }, { kind: "command", name: "reliable_commands", identifier: 134, description: "This opens a pipe to the device to provide an alternative, reliable transport of actions\n(and possibly other commands).\nThe commands are wrapped as pipe data packets.\nMultiple invocations of this command with the same `seed` are dropped\n(and thus the command is not `unique`); otherwise `seed` carries no meaning\nand should be set to a random value by the client.\nNote that while the commands sends this way are delivered exactly once, the\nresponses might get lost.", fields: [{ name: "seed", type: "u32", storage: 4, isSimpleType: true }], optional: true, hasReport: true, packFormat: "u32" }, { kind: "report", name: "reliable_commands", identifier: 134, description: "This opens a pipe to the device to provide an alternative, reliable transport of actions\n(and possibly other commands).\nThe commands are wrapped as pipe data packets.\nMultiple invocations of this command with the same `seed` are dropped\n(and thus the command is not `unique`); otherwise `seed` carries no meaning\nand should be set to a random value by the client.\nNote that while the commands sends this way are delivered exactly once, the\nresponses might get lost.", fields: [{ name: "commands", type: "pipe", storage: 12 }], secondary: true, pipeType: "reliable_commands", packFormat: "b[12]" }, { kind: "pipe_command", name: "wrapped_command", identifier: 0, description: "This opens a pipe to the device to provide an alternative, reliable transport of actions\n(and possibly other commands).\nThe commands are wrapped as pipe data packets.\nMultiple invocations of this command with the same `seed` are dropped\n(and thus the command is not `unique`); otherwise `seed` carries no meaning\nand should be set to a random value by the client.\nNote that while the commands sends this way are delivered exactly once, the\nresponses might get lost.", fields: [{ name: "service_size", type: "u8", storage: 1, isSimpleType: true }, { name: "service_index", type: "u8", storage: 1, isSimpleType: true }, { name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "payload", type: "bytes", storage: 0, isSimpleType: true }], pipeType: "reliable_commands", packFormat: "u8 u8 u16 b" }, { kind: "command", name: "standby", identifier: 135, description: "Attempt to put devices into lowest power sleep mode for a specified time - most likely involving a full reset on wake-up.", fields: [{ name: "duration", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], optional: true, packFormat: "u32" }, { kind: "rw", name: "reset_in", identifier: 128, description: "When set to value other than `0`, it asks the device to reset after specified number of microseconds.\nThis is typically used to implement watchdog functionality, where a brain device sets `reset_in` to\nsay 1.6s every 0.5s.", fields: [{ name: "_", unit: "us", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, packFormat: "u32" }, { kind: "const", name: "device_description", identifier: 384, description: "Identifies the type of hardware (eg., ACME Corp. Servo X-42 Rev C)", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, packFormat: "s" }, { kind: "const", name: "product_identifier", identifier: 385, description: "A numeric code for the string above; used to identify firmware images and devices.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true, absoluteMin: 805306368, absoluteMax: 1073741823 }], optional: true, packFormat: "u32" }, { kind: "const", name: "bootloader_product_identifier", identifier: 388, description: "Typically the same as `product_identifier` unless device was flashed by hand; the bootloader will respond to that code.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true, absoluteMin: 805306368, absoluteMax: 1073741823 }], optional: true, packFormat: "u32" }, { kind: "const", name: "firmware_version", identifier: 389, description: "A string describing firmware version; typically semver.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, packFormat: "s" }, { kind: "ro", name: "mcu_temperature", identifier: 386, description: "MCU temperature in degrees Celsius (approximate).", fields: [{ name: "_", unit: "\xB0C", type: "i16", storage: -2, isSimpleType: true, typicalMin: -10, typicalMax: 150 }], volatile: true, optional: true, preferredInterval: 6e4, packFormat: "i16" }, { kind: "ro", name: "uptime", identifier: 390, description: "Number of microseconds since boot.", fields: [{ name: "_", unit: "us", type: "u64", storage: 8, isSimpleType: true }], volatile: true, optional: true, preferredInterval: 6e4, packFormat: "u64" }], tags: ["C", "8bit"] }, { name: "Dashboard", status: "stable", shortId: "dashboard", camelName: "dashboard", shortName: "dashboard", extends: ["_base"], notes: { short: "Device that interacts, configure or inspects with the services on the bus. While a dashboard is on the bus, heuristics like device reset should be disabled." }, classIdentifier: 468029703, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }], tags: ["infrastructure"] }, { name: "DC Current Measurement", status: "experimental", shortId: "dccurrentmeasurement", camelName: "dcCurrentMeasurement", shortName: "dcCurrentMeasurement", extends: ["_base", "_sensor"], notes: { short: "A service that reports a current measurement." }, classIdentifier: 420661422, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "measurement_name", identifier: 386, description: "A string containing the net name that is being measured e.g. `POWER_DUT` or a reference e.g. `DIFF_DEV1_DEV2`. These constants can be used to identify a measurement from client code.", fields: [{ name: "_", type: "string", storage: 0 }], packFormat: "s" }, { kind: "ro", name: "measurement", identifier: 257, description: "The current measurement.", fields: [{ name: "_", unit: "A", isFloat: true, type: "f64", storage: 8 }], volatile: true, identifierName: "reading", packFormat: "f64" }, { kind: "ro", name: "measurement_error", identifier: 262, description: "Absolute error on the reading value.", fields: [{ name: "_", unit: "A", isFloat: true, type: "f64", storage: 8 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "f64" }, { kind: "const", name: "min_measurement", identifier: 260, description: "Minimum measurable current", fields: [{ name: "_", unit: "A", isFloat: true, type: "f64", storage: 8 }], optional: true, identifierName: "min_reading", packFormat: "f64" }, { kind: "const", name: "max_measurement", identifier: 261, description: "Maximum measurable current", fields: [{ name: "_", unit: "A", isFloat: true, type: "f64", storage: 8 }], optional: true, identifierName: "max_reading", packFormat: "f64" }], tags: [] }, { name: "DC Voltage Measurement", status: "experimental", shortId: "dcvoltagemeasurement", camelName: "dcVoltageMeasurement", shortName: "dcVoltageMeasurement", extends: ["_base", "_sensor"], notes: { short: "A service that reports a voltage measurement." }, classIdentifier: 372485145, enums: { VoltageMeasurementType: { name: "VoltageMeasurementType", storage: 1, members: { Absolute: 0, Differential: 1 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "measurement_type", identifier: 385, description: "The type of measurement that is taking place. Absolute results are measured with respect to ground, whereas differential results are measured against another signal that is not ground.", fields: [{ name: "_", type: "VoltageMeasurementType", storage: 1 }], packFormat: "u8" }, { kind: "const", name: "measurement_name", identifier: 386, description: "A string containing the net name that is being measured e.g. `POWER_DUT` or a reference e.g. `DIFF_DEV1_DEV2`. These constants can be used to identify a measurement from client code.", fields: [{ name: "_", type: "string", storage: 0 }], packFormat: "s" }, { kind: "ro", name: "measurement", identifier: 257, description: "The voltage measurement.", fields: [{ name: "_", unit: "V", isFloat: true, type: "f64", storage: 8 }], volatile: true, identifierName: "reading", packFormat: "f64" }, { kind: "ro", name: "measurement_error", identifier: 262, description: "Absolute error on the reading value.", fields: [{ name: "_", unit: "V", isFloat: true, type: "f64", storage: 8 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "f64" }, { kind: "const", name: "min_measurement", identifier: 260, description: "Minimum measurable current", fields: [{ name: "_", unit: "V", isFloat: true, type: "f64", storage: 8 }], optional: true, identifierName: "min_reading", packFormat: "f64" }, { kind: "const", name: "max_measurement", identifier: 261, description: "Maximum measurable current", fields: [{ name: "_", unit: "V", isFloat: true, type: "f64", storage: 8 }], optional: true, identifierName: "max_reading", packFormat: "f64" }], tags: [] }, { name: "DeviceScript Condition", status: "deprecated", shortId: "devicescriptcondition", camelName: "deviceScriptCondition", shortName: "deviceScriptCondition", extends: ["_base"], notes: { short: "Conditions are synthetic services used to synchronize threads of executions of a DeviceScript VM.\n**This is no longer used**." }, classIdentifier: 295074157, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "signal", identifier: 128, description: "Triggers a `signalled` event.", fields: [] }, { kind: "event", name: "signalled", identifier: 3, description: "Triggered by `signal` command.", fields: [], identifierName: "change" }], tags: ["infrastructure", "devicescript"], restricted: true }, { name: "DeviceScript Debugger", status: "experimental", shortId: "devicescriptdebugger", camelName: "devsDbg", shortName: "devsDbg", extends: ["_base"], notes: { short: "Allows for inspecting and affecting the state of a running DeviceScript program." }, classIdentifier: 358308672, enums: { ValueTag: { name: "ValueTag", storage: 1, members: { Number: 1, Special: 2, Fiber: 3, BuiltinObject: 5, Exotic: 6, Unhandled: 7, ImgBuffer: 32, ImgStringBuiltin: 33, ImgStringAscii: 34, ImgStringUTF8: 35, ImgRole: 48, ImgFunction: 49, ImgRoleMember: 50, ObjArray: 81, ObjMap: 82, ObjBuffer: 83, ObjString: 84, ObjStackFrame: 85, ObjPacket: 86, ObjBoundFunction: 87, ObjOpaque: 88, ObjAny: 80, ObjMask: 240, User1: 241, User2: 242, User3: 243, User4: 244 } }, ValueSpecial: { name: "ValueSpecial", storage: 1, members: { Undefined: 0, True: 1, False: 2, Null: 3, Globals: 100, CurrentException: 101 } }, FunIdx: { name: "FunIdx", storage: 2, isExtensible: true, members: { None: 0, Main: 49999, FirstBuiltIn: 5e4 } }, FiberHandle: { name: "FiberHandle", storage: 4, isExtensible: true, members: { None: 0 } }, ProgramCounter: { name: "ProgramCounter", storage: 4, isExtensible: true, members: {} }, ObjStackFrame: { name: "ObjStackFrame", storage: 4, isExtensible: true, members: { Null: 0 } }, String: { name: "String", storage: 4, isExtensible: true, members: { StaticIndicatorMask: 2147483649, StaticTagMask: 2130706432, StaticIndexMask: 16777214, Unhandled: 0 } }, StepFlags: { name: "StepFlags", storage: 2, isFlags: true, members: { StepOut: 1, StepIn: 2, Throw: 4 } }, SuspensionType: { name: "SuspensionType", storage: 1, members: { None: 0, Breakpoint: 1, UnhandledException: 2, HandledException: 3, Halt: 4, Panic: 5, Restart: 6, DebuggerStmt: 7, Step: 8 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "read_fibers", identifier: 128, description: "List the currently running fibers (threads).", fields: [{ name: "results", type: "pipe", storage: 12 }], pipeType: "read_fibers", packFormat: "b[12]" }, { kind: "pipe_report", name: "fiber", identifier: 0, description: "List the currently running fibers (threads).", fields: [{ name: "handle", type: "FiberHandle", storage: 4 }, { name: "initial_fn", type: "FunIdx", storage: 2 }, { name: "curr_fn", type: "FunIdx", storage: 2 }], pipeType: "read_fibers", packFormat: "u32 u16 u16" }, { kind: "command", name: "read_stack", identifier: 129, description: "List stack frames in a fiber.", fields: [{ name: "results", type: "pipe", storage: 12 }, { name: "fiber_handle", type: "FiberHandle", storage: 4 }], pipeType: "read_stack", packFormat: "b[12] u32" }, { kind: "pipe_report", name: "stackframe", identifier: 0, description: "List stack frames in a fiber.", fields: [{ name: "self", type: "ObjStackFrame", storage: 4 }, { name: "pc", type: "ProgramCounter", storage: 4 }, { name: "closure", type: "ObjStackFrame", storage: 4 }, { name: "fn_idx", type: "FunIdx", storage: 2 }, { name: "reserved", type: "u16", storage: 2, isSimpleType: true }], pipeType: "read_stack", packFormat: "u32 u32 u32 u16 u16" }, { kind: "command", name: "read_indexed_values", identifier: 130, description: "Read variable slots in a stack frame, elements of an array, etc.", fields: [{ name: "results", type: "pipe", storage: 12 }, { name: "v0", type: "u32", storage: 4, isSimpleType: true }, { name: "tag", type: "ValueTag", storage: 1 }, { name: "reserved", type: "u8", storage: 1, isSimpleType: true }, { name: "start", type: "u16", storage: 2, isSimpleType: true }, { name: "length", type: "u16", storage: 2, isSimpleType: true }], pipeType: "read_indexed_values", packFormat: "b[12] u32 u8 u8 u16 u16" }, { kind: "pipe_report", name: "value", identifier: 0, description: "Read variable slots in a stack frame, elements of an array, etc.", fields: [{ name: "v0", type: "u32", storage: 4, isSimpleType: true }, { name: "v1", type: "u32", storage: 4, isSimpleType: true }, { name: "fn_idx", type: "FunIdx", storage: 2 }, { name: "tag", type: "ValueTag", storage: 1 }], pipeType: "read_indexed_values", packFormat: "u32 u32 u16 u8" }, { kind: "command", name: "read_named_values", identifier: 131, description: "Read variable slots in an object.", fields: [{ name: "results", type: "pipe", storage: 12 }, { name: "v0", type: "u32", storage: 4, isSimpleType: true }, { name: "tag", type: "ValueTag", storage: 1 }], pipeType: "read_named_values", packFormat: "b[12] u32 u8" }, { kind: "pipe_report", name: "key_value", identifier: 0, description: "Read variable slots in an object.", fields: [{ name: "key", type: "String", storage: 4 }, { name: "v0", type: "u32", storage: 4, isSimpleType: true }, { name: "v1", type: "u32", storage: 4, isSimpleType: true }, { name: "fn_idx", type: "FunIdx", storage: 2 }, { name: "tag", type: "ValueTag", storage: 1 }], pipeType: "read_named_values", packFormat: "u32 u32 u32 u16 u8" }, { kind: "command", name: "read_value", identifier: 132, description: "Read a specific value.", fields: [{ name: "v0", type: "u32", storage: 4, isSimpleType: true }, { name: "tag", type: "ValueTag", storage: 1 }], hasReport: true, packFormat: "u32 u8" }, { kind: "report", name: "read_value", identifier: 132, description: "Read a specific value.", fields: [{ name: "v0", type: "u32", storage: 4, isSimpleType: true }, { name: "v1", type: "u32", storage: 4, isSimpleType: true }, { name: "fn_idx", type: "FunIdx", storage: 2 }, { name: "tag", type: "ValueTag", storage: 1 }], secondary: true, packFormat: "u32 u32 u16 u8" }, { kind: "command", name: "read_bytes", identifier: 133, description: "Read bytes of a string (UTF8) or buffer value.", fields: [{ name: "results", type: "pipe", storage: 12 }, { name: "v0", type: "u32", storage: 4, isSimpleType: true }, { name: "tag", type: "ValueTag", storage: 1 }, { name: "reserved", type: "u8", storage: 1, isSimpleType: true }, { name: "start", type: "u16", storage: 2, isSimpleType: true }, { name: "length", type: "u16", storage: 2, isSimpleType: true }], pipeType: "read_bytes", packFormat: "b[12] u32 u8 u8 u16 u16" }, { kind: "pipe_report", name: "bytes_value", identifier: 0, description: "Read bytes of a string (UTF8) or buffer value.", fields: [{ name: "data", type: "bytes", storage: 0, isSimpleType: true }], pipeType: "read_bytes", packFormat: "b" }, { kind: "command", name: "set_breakpoints", identifier: 144, description: "Set breakpoint(s) at a location(s).", fields: [{ name: "break_pc", type: "ProgramCounter", storage: 4, startRepeats: true }], packFormat: "r: u32" }, { kind: "command", name: "clear_breakpoints", identifier: 145, description: "Clear breakpoint(s) at a location(s).", fields: [{ name: "break_pc", type: "ProgramCounter", storage: 4, startRepeats: true }], packFormat: "r: u32" }, { kind: "command", name: "clear_all_breakpoints", identifier: 146, description: "Clear all breakpoints.", fields: [] }, { kind: "command", name: "resume", identifier: 147, description: "Resume program execution after a breakpoint was hit.", fields: [] }, { kind: "command", name: "halt", identifier: 148, description: "Try suspending current program. Client needs to wait for `suspended` event afterwards.", fields: [] }, { kind: "command", name: "restart_and_halt", identifier: 149, description: "Run the program from the beginning and halt on first instruction.", fields: [] }, { kind: "command", name: "step", identifier: 150, description: "Set breakpoints that only trigger in the specified stackframe and resume program.\nThe breakpoints are cleared automatically on next suspension (regardless of the reason).", fields: [{ name: "stackframe", type: "ObjStackFrame", storage: 4 }, { name: "flags", type: "StepFlags", storage: 2 }, { name: "reserved", type: "u16", storage: 2, isSimpleType: true }, { name: "break_pc", type: "ProgramCounter", storage: 4, startRepeats: true }], packFormat: "u32 u16 u16 r: u32" }, { kind: "rw", name: "enabled", identifier: 1, description: "Turn on/off the debugger interface.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "rw", name: "break_at_unhandled_exn", identifier: 128, description: "Wheather to place breakpoint at unhandled exception.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "rw", name: "break_at_handled_exn", identifier: 129, description: "Wheather to place breakpoint at handled exception.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "ro", name: "is_suspended", identifier: 384, description: "Indicates if the program is currently suspended.\nMost commands can only be executed when the program is suspended.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "event", name: "suspended", identifier: 128, description: "Emitted when the program hits a breakpoint or similar event in the specified fiber.", fields: [{ name: "fiber", type: "FiberHandle", storage: 4 }, { name: "type", type: "SuspensionType", storage: 1 }], packFormat: "u32 u8" }], tags: ["management", "devicescript"], restricted: true }, { name: "DeviceScript Manager", status: "experimental", shortId: "devicescriptmanager", camelName: "deviceScriptManager", shortName: "deviceScriptManager", extends: ["_base"], notes: { short: "Allows for deployment and control over DeviceScript virtual machine.\n\nPrograms start automatically after device restart or uploading of new program.\nYou can stop programs until next reset by setting the `running` and `autostart` registers to `false`.", events: "When program is running, `status_code == Ready`.\nWhen there is a valid program, but it is not running, `status_code == Sleeping`.\nWhen there is no valid program, `status_code == WaitingForInput`." }, classIdentifier: 288680491, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "deploy_bytecode", identifier: 128, description: "Open pipe for streaming in the bytecode of the program. The size of the bytecode has to be declared upfront.\nTo clear the program, use `bytecode_size == 0`.\nThe bytecode is streamed over regular pipe data packets.\nThe bytecode shall be fully written into flash upon closing the pipe.\nIf `autostart` is true, the program will start after being deployed.\nThe data payloads, including the last one, should have a size that is a multiple of 32 bytes.\nThus, the initial bytecode_size also needs to be a multiple of 32.", fields: [{ name: "bytecode_size", unit: "B", type: "u32", storage: 4, isSimpleType: true }], unique: true, hasReport: true, packFormat: "u32" }, { kind: "report", name: "deploy_bytecode", identifier: 128, description: "Open pipe for streaming in the bytecode of the program. The size of the bytecode has to be declared upfront.\nTo clear the program, use `bytecode_size == 0`.\nThe bytecode is streamed over regular pipe data packets.\nThe bytecode shall be fully written into flash upon closing the pipe.\nIf `autostart` is true, the program will start after being deployed.\nThe data payloads, including the last one, should have a size that is a multiple of 32 bytes.\nThus, the initial bytecode_size also needs to be a multiple of 32.", fields: [{ name: "bytecode_port", type: "pipe_port", storage: 2 }], secondary: true, pipeType: "deploy_bytecode", packFormat: "u16" }, { kind: "command", name: "read_bytecode", identifier: 129, description: "Get the current bytecode deployed on device.", fields: [{ name: "bytecode", type: "pipe", storage: 12 }], pipeType: "read_bytecode", packFormat: "b[12]" }, { kind: "pipe_report", name: "bytecode", identifier: 0, description: "Get the current bytecode deployed on device.", fields: [{ name: "data", type: "bytes", storage: 0, isSimpleType: true }], pipeType: "read_bytecode", packFormat: "b" }, { kind: "rw", name: "running", identifier: 128, description: "Indicates if the program is currently running.\nTo restart the program, stop it (write `0`), read back the register to make sure it's stopped,\nstart it, and read back.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "rw", name: "autostart", identifier: 129, description: "Indicates wheather the program should be re-started upon `reboot()` or `panic()`.\nDefaults to `true`.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "ro", name: "program_size", identifier: 384, description: "The size of current program.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "ro", name: "program_hash", identifier: 385, description: "Return FNV1A hash of the current bytecode.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "ro", name: "program_sha256", identifier: 386, description: "Return 32-byte long SHA-256 hash of the current bytecode.", fields: [{ name: "_", type: "bytes", storage: 32, isSimpleType: true, maxBytes: 32 }], packFormat: "b[32]" }, { kind: "const", name: "runtime_version", identifier: 387, description: "Returns the runtime version number compatible with [Semver](https://semver.org/).\nWhen read as 32-bit little endian integer a version `7.15.500` would be `0x07_0F_01F4`.", fields: [{ name: "patch", type: "u16", storage: 2, isSimpleType: true }, { name: "minor", type: "u8", storage: 1, isSimpleType: true }, { name: "major", type: "u8", storage: 1, isSimpleType: true }], optional: true, packFormat: "u16 u8 u8" }, { kind: "ro", name: "program_name", identifier: 388, description: "The name of currently running program. The compiler takes is from `package.json`.", fields: [{ name: "_", type: "string", storage: 0 }], packFormat: "s" }, { kind: "ro", name: "program_version", identifier: 389, description: "The version number of currently running program. The compiler takes is from `package.json`\nand `git`.", fields: [{ name: "_", type: "string", storage: 0 }], packFormat: "s" }, { kind: "event", name: "program_panic", identifier: 128, description: "Emitted when the program calls `panic(panic_code)` or `reboot()` (`panic_code == 0` in that case).\nThe byte offset in byte code of the call is given in `program_counter`.\nThe program will restart immediately when `panic_code == 0` or in a few seconds otherwise.", fields: [{ name: "panic_code", type: "u32", storage: 4, isSimpleType: true }, { name: "program_counter", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32 u32" }, { kind: "event", name: "program_change", identifier: 3, description: "Emitted after bytecode of the program has changed.", fields: [], identifierName: "change" }], tags: ["management", "devicescript"], restricted: true }, { name: "Distance", status: "stable", shortId: "distance", camelName: "distance", shortName: "distance", extends: ["_base", "_sensor"], notes: { short: "A sensor that determines the distance of an object without any physical contact involved." }, classIdentifier: 337275786, enums: { Variant: { name: "Variant", storage: 1, members: { Ultrasonic: 1, Infrared: 2, LiDAR: 3, Laser: 4 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "distance", identifier: 257, description: "Current distance from the object", fields: [{ name: "_", unit: "m", shift: 16, type: "u16.16", storage: 4, typicalMin: 0.02, typicalMax: 4 }], volatile: true, identifierName: "reading", packFormat: "u16.16" }, { kind: "ro", name: "distance_error", identifier: 262, description: "Absolute error on the reading value.", fields: [{ name: "_", unit: "m", shift: 16, type: "u16.16", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u16.16" }, { kind: "const", name: "min_range", identifier: 260, description: "Minimum measurable distance", fields: [{ name: "_", unit: "m", shift: 16, type: "u16.16", storage: 4 }], optional: true, identifierName: "min_reading", packFormat: "u16.16" }, { kind: "const", name: "max_range", identifier: 261, description: "Maximum measurable distance", fields: [{ name: "_", unit: "m", shift: 16, type: "u16.16", storage: 4 }], optional: true, identifierName: "max_reading", packFormat: "u16.16" }, { kind: "const", name: "variant", identifier: 263, description: "Determines the type of sensor used.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["8bit"] }, { name: "DMX", status: "experimental", shortId: "dmx", camelName: "dmx", shortName: "dmx", extends: ["_base"], notes: { short: "A service that can send DMX512-A packets with limited size. This service is designed to allow tinkering with a few DMX devices, but only allows 235 channels. More about DMX at https://en.wikipedia.org/wiki/DMX512." }, classIdentifier: 298814469, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "enabled", identifier: 1, description: "Determines if the DMX bridge is active.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "command", name: "send", identifier: 128, description: "Send a DMX packet, up to 236bytes long, including the start code.", fields: [{ name: "channels", type: "bytes", storage: 0, isSimpleType: true }], unique: true, packFormat: "b" }], tags: [] }, { name: "Dot Matrix", status: "rc", shortId: "dotmatrix", camelName: "dotMatrix", shortName: "dotMatrix", extends: ["_base"], notes: { short: "A rectangular dot matrix display, made of monochrome LEDs or Braille pins." }, classIdentifier: 286070091, enums: { Variant: { name: "Variant", storage: 1, members: { LED: 1, Braille: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "dots", identifier: 2, description: "The state of the screen where dot on/off state is\nstored as a bit, column by column. The column should be byte aligned.\n\nFor example, if the display has no more than 8 rows in each column, then each byte contains bits corresponding\nto a single column. Least-significant bit is on top.\nIf display has 10 rows, then each column is represented by two bytes.\nThe top-most 8 rows sit in the first byte (with the least significant bit being on top),\nand the remainign 2 row sit in the second byte.\n\nThe following C expression can be used to check if a given `column, row` coordinate is set:\n`dots[column * column_size + (row >> 3)] & (1 << (row & 7))`, where\n`column_size` is `(number_of_rows + 7) >> 3` (note that if number of rows is 8 or less then `column_size` is `1`),\nand `dots` is of `uint8_t*` type.\n\nThe size of this register is `number_of_columns * column_size` bytes.", fields: [{ name: "_", type: "bytes", storage: 0, isSimpleType: true }], identifierName: "value", packFormat: "b" }, { kind: "rw", name: "brightness", identifier: 1, description: "Reads the general brightness of the display, brightness for LEDs. `0` when the screen is off.", fields: [{ name: "_", unit: "/", shift: 8, type: "u0.8", storage: 1 }], optional: true, identifierName: "intensity", packFormat: "u0.8" }, { kind: "const", name: "rows", identifier: 385, description: "Number of rows on the screen", fields: [{ name: "_", unit: "#", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }, { kind: "const", name: "columns", identifier: 386, description: "Number of columns on the screen", fields: [{ name: "_", unit: "#", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }, { kind: "const", name: "variant", identifier: 263, description: "Describes the type of matrix used.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: [], group: "Display" }, { name: "Dual Motors", status: "experimental", shortId: "dualmotors", camelName: "dualMotors", shortName: "dualMotors", extends: ["_base"], notes: { short: "A synchronized pair of motors." }, classIdentifier: 355063095, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "speed", identifier: 2, description: "Relative speed of the motors. Use positive/negative values to run the motor forwards and backwards.\nA speed of ``0`` while ``enabled`` acts as brake.", fields: [{ name: "left", unit: "/", shift: 15, type: "i1.15", storage: -2 }, { name: "right", unit: "/", shift: 15, type: "i1.15", storage: -2 }], identifierName: "value", packFormat: "i1.15 i1.15" }, { kind: "rw", name: "enabled", identifier: 1, description: "Turn the power to the motors on/off.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "const", name: "load_torque", identifier: 384, description: "Torque required to produce the rated power of an each electrical motor at load speed.", fields: [{ name: "_", unit: "kg/cm", shift: 16, type: "u16.16", storage: 4 }], optional: true, packFormat: "u16.16" }, { kind: "const", name: "load_rotation_speed", identifier: 385, description: "Revolutions per minute of the motor under full load.", fields: [{ name: "_", unit: "rpm", shift: 16, type: "u16.16", storage: 4 }], optional: true, packFormat: "u16.16" }, { kind: "const", name: "reversible", identifier: 386, description: "Indicates if the motors can run backwards.", fields: [{ name: "_", type: "bool", storage: 1 }], optional: true, packFormat: "u8" }], tags: [], group: "Motor" }, { name: "Equivalent CO\u2082", status: "rc", shortId: "eco2", camelName: "eCO2", shortName: "eCO2", extends: ["_base", "_sensor"], notes: { short: "Measures equivalent CO\u2082 levels." }, classIdentifier: 379362758, enums: { Variant: { name: "Variant", storage: 1, members: { VOC: 1, NDIR: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "e_CO2", identifier: 257, description: "Equivalent CO\u2082 (eCO\u2082) readings.", fields: [{ name: "_", unit: "ppm", shift: 10, type: "u22.10", storage: 4, typicalMin: 400, typicalMax: 8192 }], volatile: true, identifierName: "reading", preferredInterval: 1e3, packFormat: "u22.10" }, { kind: "ro", name: "e_CO2_error", identifier: 262, description: "Error on the reading value.", fields: [{ name: "_", unit: "ppm", shift: 10, type: "u22.10", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u22.10" }, { kind: "const", name: "min_e_CO2", identifier: 260, description: "Minimum measurable value", fields: [{ name: "_", unit: "ppm", shift: 10, type: "u22.10", storage: 4 }], identifierName: "min_reading", packFormat: "u22.10" }, { kind: "const", name: "max_e_CO2", identifier: 261, description: "Minimum measurable value", fields: [{ name: "_", unit: "ppm", shift: 10, type: "u22.10", storage: 4 }], identifierName: "max_reading", packFormat: "u22.10" }, { kind: "const", name: "variant", identifier: 263, description: "Type of physical sensor and capabilities.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["8bit"], group: "Environment" }, { name: "Flex", status: "rc", shortId: "flex", camelName: "flex", shortName: "flex", extends: ["_base", "_sensor"], notes: { short: "A bending or deflection sensor." }, classIdentifier: 524797638, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "bending", identifier: 257, description: "A measure of the bending.", fields: [{ name: "_", unit: "/", shift: 15, type: "i1.15", storage: -2 }], volatile: true, identifierName: "reading", packFormat: "i1.15" }, { kind: "const", name: "length", identifier: 384, description: "Length of the flex sensor", fields: [{ name: "_", unit: "mm", type: "u16", storage: 2, isSimpleType: true }], optional: true, packFormat: "u16" }], tags: ["C", "8bit"], group: "Sensor" }, { name: "Gamepad", status: "rc", shortId: "gamepad", camelName: "gamepad", shortName: "gamepad", extends: ["_base", "_sensor"], notes: { short: "A two axis directional gamepad with optional buttons." }, classIdentifier: 277836886, enums: { Buttons: { name: "Buttons", storage: 4, isFlags: true, members: { Left: 1, Up: 2, Right: 4, Down: 8, A: 16, B: 32, Menu: 64, Select: 128, Reset: 256, Exit: 512, X: 1024, Y: 2048 } }, Variant: { name: "Variant", storage: 1, members: { Thumb: 1, ArcadeBall: 2, ArcadeStick: 3, Gamepad: 4 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "direction", identifier: 257, description: 'If the gamepad is analog, the directional buttons should be "simulated", based on gamepad position\n(`Left` is `{ x = -1, y = 0 }`, `Up` is `{ x = 0, y = -1}`).\nIf the gamepad is digital, then each direction will read as either `-1`, `0`, or `1` (in fixed representation).\nThe primary button on the gamepad is `A`.', fields: [{ name: "buttons", type: "Buttons", storage: 4 }, { name: "x", unit: "/", shift: 15, type: "i1.15", storage: -2 }, { name: "y", unit: "/", shift: 15, type: "i1.15", storage: -2 }], volatile: true, identifierName: "reading", packFormat: "u32 i1.15 i1.15" }, { kind: "const", name: "variant", identifier: 263, description: "The type of physical gamepad.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }, { kind: "const", name: "buttons_available", identifier: 384, description: "Indicates a bitmask of the buttons that are mounted on the gamepad.\nIf the `Left`/`Up`/`Right`/`Down` buttons are marked as available here, the gamepad is digital.\nEven when marked as not available, they will still be simulated based on the analog gamepad.", fields: [{ name: "_", type: "Buttons", storage: 4 }], packFormat: "u32" }, { kind: "event", name: "buttons_changed", identifier: 3, description: "Emitted whenever the state of buttons changes.", fields: [{ name: "buttons", type: "Buttons", storage: 4 }], identifierName: "change", packFormat: "u32" }], tags: ["8bit", "padauk"], group: "Button" }, { name: "GPIO", status: "experimental", shortId: "gpio", camelName: "GPIO", shortName: "GPIO", extends: ["_base", "_sensor"], notes: { short: "Access to General Purpose Input/Output (GPIO) pins on a board.\nThe pins are indexed `0 ... num_pins-1`.\nThe indexing does not correspond to hardware pin names, nor labels on the board (see `get_pin_info` command for that),\nand should **not** be exposed to the user." }, classIdentifier: 282614377, enums: { Mode: { name: "Mode", storage: 1, members: { Off: 0, OffPullUp: 16, OffPullDown: 32, Input: 1, InputPullUp: 17, InputPullDown: 33, Output: 2, OutputHigh: 18, OutputLow: 34, AnalogIn: 3, Alternative: 4, BaseModeMask: 15 } }, Capabilities: { name: "Capabilities", storage: 2, isFlags: true, members: { PullUp: 1, PullDown: 2, Input: 4, Output: 8, Analog: 16 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "state", identifier: 257, description: "For every pin set to `Input*` the corresponding **bit** in `digital_values` will be `1` if and only if\nthe pin is high.\nFor other pins, the bit is `0`.\nThis is normally streamed at low-ish speed, but it's also automatically reported whenever\na digital input pin changes value (throttled to ~100Hz).\nThe analog values can be read with the `ADC` service.", fields: [{ name: "digital_values", type: "bytes", storage: 0, isSimpleType: true }], volatile: true, identifierName: "reading", packFormat: "b" }, { kind: "ro", name: "num_pins", identifier: 384, description: "Number of pins that can be operated through this service.", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true, absoluteMax: 128, absoluteMin: 0 }], packFormat: "u8" }, { kind: "command", name: "configure", identifier: 128, description: "Configure (including setting the value) zero or more pins.\n`Alternative` settings means the pin is controlled by other service (SPI, I2C, UART, PWM, etc.).", fields: [{ name: "pin", type: "u8", storage: 1, isSimpleType: true, startRepeats: true }, { name: "mode", type: "Mode", storage: 1 }], packFormat: "r: u8 u8" }, { kind: "command", name: "pin_info", identifier: 129, description: "Report capabilities and name of a pin.", fields: [{ name: "pin", type: "u8", storage: 1, isSimpleType: true }], hasReport: true, packFormat: "u8" }, { kind: "report", name: "pin_info", identifier: 129, description: "Report capabilities and name of a pin.", fields: [{ name: "pin", type: "u8", storage: 1, isSimpleType: true }, { name: "hw_pin", type: "u8", storage: 1, isSimpleType: true }, { name: "capabilities", type: "Capabilities", storage: 2 }, { name: "mode", type: "Mode", storage: 1 }, { name: "label", type: "string", storage: 0 }], secondary: true, packFormat: "u8 u8 u16 u8 s" }, { kind: "command", name: "pin_by_label", identifier: 131, description: "This responds with `pin_info` report.", fields: [{ name: "label", type: "string", storage: 0 }], hasReport: true, packFormat: "s" }, { kind: "report", name: "pin_by_label", identifier: 131, description: "This responds with `pin_info` report.", fields: [{ name: "pin", type: "u8", storage: 1, isSimpleType: true }, { name: "hw_pin", type: "u8", storage: 1, isSimpleType: true }, { name: "capabilities", type: "Capabilities", storage: 2 }, { name: "mode", type: "Mode", storage: 1 }, { name: "label", type: "string", storage: 0 }], secondary: true, packFormat: "u8 u8 u16 u8 s" }, { kind: "command", name: "pin_by_hw_pin", identifier: 132, description: "This responds with `pin_info` report.", fields: [{ name: "hw_pin", type: "u8", storage: 1, isSimpleType: true }], hasReport: true, packFormat: "u8" }, { kind: "report", name: "pin_by_hw_pin", identifier: 132, description: "This responds with `pin_info` report.", fields: [{ name: "pin", type: "u8", storage: 1, isSimpleType: true }, { name: "hw_pin", type: "u8", storage: 1, isSimpleType: true }, { name: "capabilities", type: "Capabilities", storage: 2 }, { name: "mode", type: "Mode", storage: 1 }, { name: "label", type: "string", storage: 0 }], secondary: true, packFormat: "u8 u8 u16 u8 s" }], tags: ["io"] }, { name: "Gyroscope", status: "rc", shortId: "gyroscope", camelName: "gyroscope", shortName: "gyroscope", extends: ["_base", "_sensor"], notes: { short: "A 3-axis gyroscope." }, classIdentifier: 505087730, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "rotation_rates", identifier: 257, description: "Indicates the current rates acting on gyroscope.", fields: [{ name: "x", unit: "\xB0/s", shift: 20, type: "i12.20", storage: -4 }, { name: "y", unit: "\xB0/s", shift: 20, type: "i12.20", storage: -4 }, { name: "z", unit: "\xB0/s", shift: 20, type: "i12.20", storage: -4 }], volatile: true, identifierName: "reading", packFormat: "i12.20 i12.20 i12.20" }, { kind: "ro", name: "rotation_rates_error", identifier: 262, description: "Error on the reading value.", fields: [{ name: "_", unit: "\xB0/s", shift: 20, type: "u12.20", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u12.20" }, { kind: "rw", name: "max_rate", identifier: 8, description: 'Configures the range of rotation rates.\nThe value will be "rounded up" to one of `max_rates_supported`.', fields: [{ name: "_", unit: "\xB0/s", shift: 20, type: "u12.20", storage: 4 }], optional: true, identifierName: "reading_range", packFormat: "u12.20" }, { kind: "const", name: "max_rates_supported", identifier: 266, description: "Lists values supported for writing `max_rate`.", fields: [{ name: "max_rate", unit: "\xB0/s", shift: 20, type: "u12.20", storage: 4, startRepeats: true }], optional: true, identifierName: "supported_ranges", packFormat: "r: u12.20" }], tags: [], group: "Movement" }, { name: "Heart Rate", status: "experimental", shortId: "heartrate", camelName: "heartRate", shortName: "heartRate", extends: ["_base", "_sensor"], notes: { short: "A sensor approximating the heart rate. \n\n\n**Jacdac is NOT suitable for medical devices and should NOT be used in any kind of device to diagnose or treat any medical conditions.**" }, classIdentifier: 376204740, enums: { Variant: { name: "Variant", storage: 1, members: { Finger: 1, Chest: 2, Wrist: 3, Pump: 4, WebCam: 5 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "heart_rate", identifier: 257, description: "The estimated heart rate.", fields: [{ name: "_", unit: "bpm", shift: 16, type: "u16.16", storage: 4, typicalMin: 30, typicalMax: 200 }], volatile: true, identifierName: "reading", preferredInterval: 1e3, packFormat: "u16.16" }, { kind: "ro", name: "heart_rate_error", identifier: 262, description: "The estimated error on the reported sensor data.", fields: [{ name: "_", unit: "bpm", shift: 16, type: "u16.16", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u16.16" }, { kind: "const", name: "variant", identifier: 263, description: "The type of physical sensor", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["8bit"], group: "Biometric" }, { name: "HID Joystick", status: "experimental", shortId: "hidjoystick", camelName: "hidJoystick", shortName: "hidJoystick", extends: ["_base"], notes: { short: "Controls a HID joystick." }, classIdentifier: 437330261, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "button_count", identifier: 384, description: "Number of button report supported", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "const", name: "buttons_analog", identifier: 385, description: "A bitset that indicates which button is analog.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "const", name: "axis_count", identifier: 386, description: "Number of analog input supported", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "command", name: "set_buttons", identifier: 128, description: "Sets the up/down button state, one byte per button, supports analog buttons. For digital buttons, use `0` for released, `1` for pressed.", fields: [{ name: "pressure", unit: "/", shift: 8, type: "u0.8", storage: 1, startRepeats: true }], unique: true, packFormat: "r: u0.8" }, { kind: "command", name: "set_axis", identifier: 129, description: "Sets the state of analog inputs.", fields: [{ name: "position", unit: "/", shift: 15, type: "i1.15", storage: -2, startRepeats: true }], unique: true, packFormat: "r: i1.15" }], tags: [] }, { name: "HID Keyboard", status: "stable", shortId: "hidkeyboard", camelName: "hidKeyboard", shortName: "hidKeyboard", extends: ["_base"], notes: { short: "Control a HID keyboard.\n\nThe codes for the key (selectors) is defined in the [HID Keyboard\nspecification](https://usb.org/sites/default/files/hut1_21.pdf), chapter 10 Keyboard/Keypad Page, page 81.\nModifiers are in page 87.\n\nThe device keeps tracks of the key state and is able to clear it all with the clear command." }, classIdentifier: 414210922, enums: { Selector: { name: "Selector", storage: 2, members: { None: 0, ErrorRollOver: 1, PostFail: 2, ErrorUndefined: 3, A: 4, B: 5, C: 6, D: 7, E: 8, F: 9, G: 10, H: 11, I: 12, J: 13, K: 14, L: 15, M: 16, N: 17, O: 18, P: 19, Q: 20, R: 21, S: 22, T: 23, U: 24, V: 25, W: 26, X: 27, Y: 28, Z: 29, _1: 30, _2: 31, _3: 32, _4: 33, _5: 34, _6: 35, _7: 36, _8: 37, _9: 38, _0: 39, Return: 40, Escape: 41, Backspace: 42, Tab: 43, Spacebar: 44, Minus: 45, Equals: 46, LeftSquareBracket: 47, RightSquareBracket: 48, Backslash: 49, NonUsHash: 50, Semicolon: 51, Quote: 52, GraveAccent: 53, Comma: 54, Period: 55, Slash: 56, CapsLock: 57, F1: 58, F2: 59, F3: 60, F4: 61, F5: 62, F6: 63, F7: 64, F8: 65, F9: 66, F10: 67, F11: 68, F12: 69, PrintScreen: 70, ScrollLock: 71, Pause: 72, Insert: 73, Home: 74, PageUp: 75, Delete: 76, End: 77, PageDown: 78, RightArrow: 79, LeftArrow: 80, DownArrow: 81, UpArrow: 82, KeypadNumLock: 83, KeypadDivide: 84, KeypadMultiply: 85, KeypadAdd: 86, KeypadSubtrace: 87, KeypadReturn: 88, Keypad1: 89, Keypad2: 90, Keypad3: 91, Keypad4: 92, Keypad5: 93, Keypad6: 94, Keypad7: 95, Keypad8: 96, Keypad9: 97, Keypad0: 98, KeypadDecimalPoint: 99, NonUsBackslash: 100, Application: 101, Power: 102, KeypadEquals: 103, F13: 104, F14: 105, F15: 106, F16: 107, F17: 108, F18: 109, F19: 110, F20: 111, F21: 112, F22: 113, F23: 114, F24: 115, Execute: 116, Help: 117, Menu: 118, Select: 119, Stop: 120, Again: 121, Undo: 122, Cut: 123, Copy: 124, Paste: 125, Find: 126, Mute: 127, VolumeUp: 128, VolumeDown: 129 } }, Modifiers: { name: "Modifiers", storage: 1, isFlags: true, members: { None: 0, LeftControl: 1, LeftShift: 2, LeftAlt: 4, LeftGUI: 8, RightControl: 16, RightShift: 32, RightAlt: 64, RightGUI: 128 } }, Action: { name: "Action", storage: 1, members: { Press: 0, Up: 1, Down: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "key", identifier: 128, description: "Presses a key or a sequence of keys down.", fields: [{ name: "selector", type: "Selector", storage: 2, startRepeats: true }, { name: "modifiers", type: "Modifiers", storage: 1 }, { name: "action", type: "Action", storage: 1 }], lowLevel: true, unique: true, packFormat: "r: u16 u8 u8" }, { kind: "command", name: "clear", identifier: 129, description: "Clears all pressed keys.", fields: [] }], tags: ["8bit"] }, { name: "HID Mouse", status: "stable", shortId: "hidmouse", camelName: "hidMouse", shortName: "hidMouse", extends: ["_base"], notes: { short: "Controls a HID mouse." }, classIdentifier: 411425820, enums: { Button: { name: "Button", storage: 2, isFlags: true, members: { Left: 1, Right: 2, Middle: 4 } }, ButtonEvent: { name: "ButtonEvent", storage: 1, members: { Up: 1, Down: 2, Click: 3, DoubleClick: 4 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "set_button", identifier: 128, description: "Sets the up/down state of one or more buttons.\nA `Click` is the same as `Down` followed by `Up` after 100ms.\nA `DoubleClick` is two clicks with `150ms` gap between them (that is, `100ms` first click, `150ms` gap, `100ms` second click).", fields: [{ name: "buttons", type: "Button", storage: 2 }, { name: "ev", type: "ButtonEvent", storage: 1 }], unique: true, packFormat: "u16 u8" }, { kind: "command", name: "move", identifier: 129, description: "Moves the mouse by the distance specified.\nIf the time is positive, it specifies how long to make the move.", fields: [{ name: "dx", unit: "#", type: "i16", storage: -2, isSimpleType: true }, { name: "dy", unit: "#", type: "i16", storage: -2, isSimpleType: true }, { name: "time", unit: "ms", type: "u16", storage: 2, isSimpleType: true }], unique: true, packFormat: "i16 i16 u16" }, { kind: "command", name: "wheel", identifier: 130, description: "Turns the wheel up or down. Positive if scrolling up.\nIf the time is positive, it specifies how long to make the move.", fields: [{ name: "dy", unit: "#", type: "i16", storage: -2, isSimpleType: true }, { name: "time", unit: "ms", type: "u16", storage: 2, isSimpleType: true }], unique: true, packFormat: "i16 u16" }], tags: ["8bit"] }, { name: "Humidity", status: "stable", shortId: "humidity", camelName: "humidity", shortName: "humidity", extends: ["_base", "_sensor"], notes: { short: "A sensor measuring humidity of outside environment." }, classIdentifier: 382210232, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "humidity", identifier: 257, description: "The relative humidity in percentage of full water saturation.", fields: [{ name: "_", unit: "%RH", shift: 10, type: "u22.10", storage: 4 }], volatile: true, identifierName: "reading", preferredInterval: 5e3, packFormat: "u22.10" }, { kind: "ro", name: "humidity_error", identifier: 262, description: "The real humidity is between `humidity - humidity_error` and `humidity + humidity_error`.", fields: [{ name: "_", unit: "%RH", shift: 10, type: "u22.10", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u22.10" }, { kind: "const", name: "min_humidity", identifier: 260, description: "Lowest humidity that can be reported.", fields: [{ name: "_", unit: "%RH", shift: 10, type: "u22.10", storage: 4 }], identifierName: "min_reading", packFormat: "u22.10" }, { kind: "const", name: "max_humidity", identifier: 261, description: "Highest humidity that can be reported.", fields: [{ name: "_", unit: "%RH", shift: 10, type: "u22.10", storage: 4 }], identifierName: "max_reading", packFormat: "u22.10" }], tags: ["C", "8bit"], group: "Environment" }, { name: "I2C", status: "experimental", shortId: "i2c", camelName: "I2C", shortName: "I2C", extends: ["_base"], notes: { short: "Inter-Integrated Circuit (I2C, I\xB2C, IIC) serial communication bus lets you communicate with\nmany sensors and actuators." }, classIdentifier: 471386691, enums: { Status: { name: "Status", storage: 1, members: { OK: 0, NAckAddr: 1, NAckData: 2, NoI2C: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "ro", name: "ok", identifier: 384, description: "Indicates whether the I2C is working.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "command", name: "transaction", identifier: 128, description: "`address` is 7-bit.\n`num_read` can be 0 if nothing needs to be read.\nThe `write_buf` includes the register address if required (first one or two bytes).", fields: [{ name: "address", type: "u8", storage: 1, isSimpleType: true }, { name: "num_read", unit: "B", type: "u8", storage: 1, isSimpleType: true }, { name: "write_buf", type: "bytes", storage: 0, isSimpleType: true }], unique: true, hasReport: true, packFormat: "u8 u8 b" }, { kind: "report", name: "transaction", identifier: 128, description: "`address` is 7-bit.\n`num_read` can be 0 if nothing needs to be read.\nThe `write_buf` includes the register address if required (first one or two bytes).", fields: [{ name: "status", type: "Status", storage: 1 }, { name: "read_buf", type: "bytes", storage: 0, isSimpleType: true }], secondary: true, packFormat: "u8 b" }], tags: ["io"] }, { name: "Illuminance", status: "rc", shortId: "illuminance", camelName: "illuminance", shortName: "illuminance", extends: ["_base", "_sensor"], notes: { short: "Detects the amount of light falling onto a given surface area.\n\nNote that this is different from _luminance_, the amount of light that passes through, emits from, or reflects off an object." }, classIdentifier: 510577394, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "illuminance", identifier: 257, description: "The amount of illuminance, as lumens per square metre.", fields: [{ name: "_", unit: "lux", shift: 10, type: "u22.10", storage: 4, typicalMax: 1e5, typicalMin: 0 }], volatile: true, identifierName: "reading", packFormat: "u22.10" }, { kind: "ro", name: "illuminance_error", identifier: 262, description: "Error on the reported sensor value.", fields: [{ name: "_", unit: "lux", shift: 10, type: "u22.10", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u22.10" }], tags: ["8bit", "padauk"], group: "Environment" }, { name: "Indexed screen", status: "rc", shortId: "indexedscreen", camelName: "indexedScreen", shortName: "indexedScreen", extends: ["_base"], notes: { short: "A screen with indexed colors from a palette.\n\nThis is often run over an SPI connection or directly on the MCU, not regular single-wire Jacdac." }, classIdentifier: 385496805, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "start_update", identifier: 129, description: "Sets the update window for subsequent `set_pixels` commands.", fields: [{ name: "x", unit: "px", type: "u16", storage: 2, isSimpleType: true }, { name: "y", unit: "px", type: "u16", storage: 2, isSimpleType: true }, { name: "width", unit: "px", type: "u16", storage: 2, isSimpleType: true }, { name: "height", unit: "px", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16 u16 u16 u16" }, { kind: "command", name: "set_pixels", identifier: 131, description: 'Set pixels in current window, according to current palette.\nEach "line" of data is aligned to a byte.', fields: [{ name: "pixels", type: "bytes", storage: 0, isSimpleType: true }], unique: true, packFormat: "b" }, { kind: "rw", name: "brightness", identifier: 1, description: "Set backlight brightness.\nIf set to `0` the display may go to sleep.", fields: [{ name: "_", unit: "/", shift: 8, type: "u0.8", storage: 1 }], identifierName: "intensity", packFormat: "u0.8" }, { kind: "rw", name: "palette", identifier: 128, description: "The current palette. The colors are `[r,g,b, padding]` 32bit color entries.\nThe color entry repeats `1 << bits_per_pixel` times.\nThis register may be write-only.", fields: [{ name: "_", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "b" }, { kind: "const", name: "bits_per_pixel", identifier: 384, description: "Determines the number of palette entries.\nTypical values are 1 or 4.", fields: [{ name: "_", unit: "bit", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "const", name: "width", identifier: 385, description: 'Screen width in "natural" orientation.', fields: [{ name: "_", unit: "px", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }, { kind: "const", name: "height", identifier: 386, description: 'Screen height in "natural" orientation.', fields: [{ name: "_", unit: "px", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }, { kind: "rw", name: "width_major", identifier: 129, description: 'If true, consecutive pixels in the "width" direction are sent next to each other (this is typical for graphics cards).\nIf false, consecutive pixels in the "height" direction are sent next to each other.\nFor embedded screen controllers, this is typically true iff `width < height`\n(in other words, it\'s only true for portrait orientation screens).\nSome controllers may allow the user to change this (though the refresh order may not be optimal then).\nThis is independent of the `rotation` register.', fields: [{ name: "_", type: "bool", storage: 1 }], optional: true, packFormat: "u8" }, { kind: "rw", name: "up_sampling", identifier: 130, description: "Every pixel sent over wire is represented by `up_sampling x up_sampling` square of physical pixels.\nSome displays may allow changing this (which will also result in changes to `width` and `height`).\nTypical values are 1 and 2.", fields: [{ name: "_", unit: "px", type: "u8", storage: 1, isSimpleType: true }], optional: true, packFormat: "u8" }, { kind: "rw", name: "rotation", identifier: 131, description: "Possible values are 0, 90, 180 and 270 only.\nWrite to this register do not affect `width` and `height` registers,\nand may be ignored by some screens.", fields: [{ name: "_", unit: "\xB0", type: "u16", storage: 2, isSimpleType: true }], optional: true, packFormat: "u16" }], tags: ["SPI"] }, { name: "Infrastructure", status: "stable", shortId: "infrastructure", camelName: "infrastructure", shortName: "infrastructure", extends: ["_base"], notes: { short: "A service that tags a device as purely infrastructure device.\n\n\nA Jacdac user interface can hide any device that hosts this service." }, classIdentifier: 504728043, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }], tags: ["infrastructure"] }, { name: "Keyboard client", status: "experimental", shortId: "keyboardclient", camelName: "keyboardClient", shortName: "keyboardClient", extends: ["_base"], notes: { short: "Measures KeyboardClient." }, classIdentifier: 289210942, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "event", name: "down", identifier: 1, description: "Emitted when a key is pressed.", fields: [{ name: "key", type: "u16", storage: 2, isSimpleType: true }], identifierName: "active", packFormat: "u16" }, { kind: "event", name: "hold", identifier: 129, description: "Emitted when a key is held.", fields: [{ name: "key", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }], tags: [] }, { name: "LED", status: "stable", shortId: "led", camelName: "led", shortName: "led", extends: ["_base"], notes: { short: "A controller for displays of individually controlled RGB LEDs.\n\nFor 64 or less LEDs, the service should support the pack the pixels in the pixels register.\nBeyond this size, the register should return an empty payload as the amount of data exceeds\nthe size of a packet. Typically services that use more than 64 LEDs\nwill run on the same MCU and will maintain the pixels buffer internally." }, classIdentifier: 369743088, enums: { Variant: { name: "Variant", storage: 1, members: { Strip: 1, Ring: 2, Stick: 3, Jewel: 4, Matrix: 5 } } }, constants: { max_pixels_length: { value: 64, hex: false } }, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "pixels", identifier: 2, description: "A buffer of 24bit RGB color entries for each LED, in R, G, B order.\nWhen writing, if the buffer is too short, the remaining pixels are set to `#000000`;\nIf the buffer is too long, the write may be ignored, or the additional pixels may be ignored.\nIf the number of pixels is greater than `max_pixels_length`, the read should return an empty payload.", fields: [{ name: "_", type: "bytes", storage: 0, isSimpleType: true }], identifierName: "value", packFormat: "b" }, { kind: "rw", name: "brightness", identifier: 1, description: "Set the luminosity of the strip.\nAt `0` the power to the strip is completely shut down.", fields: [{ name: "_", unit: "/", shift: 8, type: "u0.8", storage: 1, defaultValue: 0.05 }], identifierName: "intensity", packFormat: "u0.8" }, { kind: "ro", name: "actual_brightness", identifier: 384, description: "This is the luminosity actually applied to the strip.\nMay be lower than `brightness` if power-limited by the `max_power` register.\nIt will rise slowly (few seconds) back to `brightness` is limits are no longer required.", fields: [{ name: "_", unit: "/", shift: 8, type: "u0.8", storage: 1 }], packFormat: "u0.8" }, { kind: "const", name: "num_pixels", identifier: 386, description: "Specifies the number of pixels in the strip.", fields: [{ name: "_", unit: "#", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }, { kind: "const", name: "num_columns", identifier: 387, description: "If the LED pixel strip is a matrix, specifies the number of columns.", fields: [{ name: "_", unit: "#", type: "u16", storage: 2, isSimpleType: true }], optional: true, packFormat: "u16" }, { kind: "rw", name: "max_power", identifier: 7, description: "Limit the power drawn by the light-strip (and controller).", fields: [{ name: "_", unit: "mA", type: "u16", storage: 2, isSimpleType: true, defaultValue: 200 }], optional: true, identifierName: "max_power", packFormat: "u16" }, { kind: "const", name: "leds_per_pixel", identifier: 388, description: "If known, specifies the number of LEDs in parallel on this device.\nThe actual number of LEDs is `num_pixels * leds_per_pixel`.", fields: [{ name: "_", unit: "#", type: "u16", storage: 2, isSimpleType: true }], optional: true, packFormat: "u16" }, { kind: "const", name: "wave_length", identifier: 389, description: "If monochrome LED, specifies the wave length of the LED.\nRegister is missing for RGB LEDs.", fields: [{ name: "_", unit: "nm", type: "u16", storage: 2, isSimpleType: true, typicalMin: 365, typicalMax: 885 }], optional: true, packFormat: "u16" }, { kind: "const", name: "luminous_intensity", identifier: 390, description: "The luminous intensity of all the LEDs, at full brightness, in micro candella.", fields: [{ name: "_", unit: "mcd", type: "u16", storage: 2, isSimpleType: true, typicalMin: 10, typicalMax: 5e3 }], optional: true, packFormat: "u16" }, { kind: "const", name: "variant", identifier: 263, description: "Specifies the shape of the light strip.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: [], group: "Light" }, { name: "LED Single", status: "deprecated", shortId: "ledsingle", camelName: "ledSingle", shortName: "ledSingle", extends: ["_base"], notes: { short: "A controller for 1 or more monochrome or RGB LEDs connected in parallel." }, classIdentifier: 506480888, enums: { Variant: { name: "Variant", storage: 1, members: { ThroughHole: 1, SMD: 2, Power: 3, Bead: 4 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "animate", identifier: 128, description: "This has the same semantics as `set_status_light` in the control service.", fields: [{ name: "to_red", type: "u8", storage: 1, isSimpleType: true }, { name: "to_green", type: "u8", storage: 1, isSimpleType: true }, { name: "to_blue", type: "u8", storage: 1, isSimpleType: true }, { name: "speed", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8 u8 u8 u8" }, { kind: "ro", name: "color", identifier: 384, description: "The current color of the LED.", fields: [{ name: "red", type: "u8", storage: 1, isSimpleType: true }, { name: "green", type: "u8", storage: 1, isSimpleType: true }, { name: "blue", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8 u8 u8" }, { kind: "rw", name: "max_power", identifier: 7, description: "Limit the power drawn by the light-strip (and controller).", fields: [{ name: "_", unit: "mA", type: "u16", storage: 2, isSimpleType: true, defaultValue: 100 }], optional: true, identifierName: "max_power", packFormat: "u16" }, { kind: "const", name: "led_count", identifier: 387, description: "If known, specifies the number of LEDs in parallel on this device.", fields: [{ name: "_", type: "u16", storage: 2, isSimpleType: true }], optional: true, packFormat: "u16" }, { kind: "const", name: "wave_length", identifier: 385, description: "If monochrome LED, specifies the wave length of the LED.", fields: [{ name: "_", unit: "nm", type: "u16", storage: 2, isSimpleType: true, typicalMin: 365, typicalMax: 885 }], optional: true, packFormat: "u16" }, { kind: "const", name: "luminous_intensity", identifier: 386, description: "The luminous intensity of the LED, at full value, in micro candella.", fields: [{ name: "_", unit: "mcd", type: "u16", storage: 2, isSimpleType: true, typicalMin: 10, typicalMax: 5e3 }], optional: true, packFormat: "u16" }, { kind: "const", name: "variant", identifier: 263, description: "The physical type of LED.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["8bit", "padauk"], group: "Light" }, { name: "LED Strip", status: "stable", shortId: "ledstrip", camelName: "ledStrip", shortName: "ledStrip", extends: ["_base"], notes: { short: "A controller for strips of individually controlled RGB LEDs.", long: "## Light programs\n\nWith 1 mbit Jacdac, we can transmit under 2k of data per animation frame (at 20fps).\nIf transmitting raw data that would be around 500 pixels, which is not enough for many\ninstallations and it would completely clog the network.\n\nThus, light service defines a domain-specific language for describing light animations\nand efficiently transmitting them over wire. For short LED displays, less than 64 LEDs, \nyou can also use the [LED service](/services/led).\n\nLight commands are not Jacdac commands.\nLight commands are efficiently encoded as sequences of bytes and typically sent as payload\nof `run` command.\n\nDefinitions:\n\n- `P` - position in the strip\n- `R` - number of repetitions of the command\n- `N` - number of pixels affected by the command\n- `C` - single color designation\n- `C+` - sequence of color designations\n\nUpdate modes:\n\n- `0` - replace\n- `1` - add RGB\n- `2` - subtract RGB\n- `3` - multiply RGB (by c/128); each pixel value will change by at least 1\n\nProgram commands:\n\n- `0xD0: setall C+` - set all pixels in current range to given color pattern\n- `0xD1: fade C+` - set pixels in current range to colors between colors in sequence\n- `0xD2: fadehsv C+` - similar to `fade()`, but colors are specified and faded in HSV\n- `0xD3: rotfwd K` - rotate (shift) pixels by `K` positions away from the connector\n- `0xD4: rotback K` - same, but towards the connector\n- `0xD5: show M=50` - send buffer to strip and wait `M` milliseconds\n- `0xD6: range P=0 N=length W=1 S=0` - range from pixel `P`, `N` pixels long (currently unsupported: every `W` pixels skip `S` pixels)\n- `0xD7: mode K=0` - set update mode\n- `0xD8: tmpmode K=0` - set update mode for next command only\n- `0xCF: setone P C` - set one pixel at `P` (in current range) to given color\n- `mult V` - macro to multiply current range by given value (float)\n\nA number `k` is encoded as follows:\n\n- `0 <= k < 128` -> `k`\n- `128 <= k < 16383` -> `0x80 | (k >> 8), k & 0xff`\n- bigger and negative numbers are not supported\n\nThus, bytes `0xC0-0xFF` are free to use for commands.\n\nFormats:\n\n- `0xC1, R, G, B` - single color parameter\n- `0xC2, R0, G0, B0, R1, G1, B1` - two color parameter\n- `0xC3, R0, G0, B0, R1, G1, B1, R2, G2, B2` - three color parameter\n- `0xC0, N, R0, G0, B0, ..., R(N-1), G(N-1), B(N-1)` - `N` color parameter\n- `0xCF, , R, G, B` - `set1` special format\n\nCommands are encoded as command byte, followed by parameters in the order\nfrom the command definition.\n\nThe `setone()` command has irregular encoding to save space - it is byte `0xCF` followed by encoded\nnumber, and followed by 3 bytes of color." }, classIdentifier: 309264608, enums: { LightType: { name: "LightType", storage: 1, members: { WS2812B_GRB: 0, APA102: 16, SK9822: 17 } }, Variant: { name: "Variant", storage: 1, members: { Strip: 1, Ring: 2, Stick: 3, Jewel: 4, Matrix: 5 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "brightness", identifier: 1, description: "Set the luminosity of the strip.\nAt `0` the power to the strip is completely shut down.", fields: [{ name: "_", unit: "/", shift: 8, type: "u0.8", storage: 1, defaultValue: 0.05 }], identifierName: "intensity", packFormat: "u0.8" }, { kind: "ro", name: "actual_brightness", identifier: 384, description: "This is the luminosity actually applied to the strip.\nMay be lower than `brightness` if power-limited by the `max_power` register.\nIt will rise slowly (few seconds) back to `brightness` is limits are no longer required.", fields: [{ name: "_", unit: "/", shift: 8, type: "u0.8", storage: 1 }], packFormat: "u0.8" }, { kind: "rw", name: "light_type", identifier: 128, description: "Specifies the type of light strip connected to controller.\nControllers which are sold with lights should default to the correct type\nand could not allow change.", fields: [{ name: "_", type: "LightType", storage: 1 }], packFormat: "u8" }, { kind: "rw", name: "num_pixels", identifier: 129, description: "Specifies the number of pixels in the strip.\nControllers which are sold with lights should default to the correct length\nand could not allow change. Increasing length at runtime leads to ineffective use of memory and may lead to controller reboot.", fields: [{ name: "_", unit: "#", type: "u16", storage: 2, isSimpleType: true, defaultValue: 15 }], packFormat: "u16" }, { kind: "rw", name: "num_columns", identifier: 131, description: "If the LED pixel strip is a matrix, specifies the number of columns. Otherwise, a square shape is assumed. Controllers which are sold with lights should default to the correct length\nand could not allow change. Increasing length at runtime leads to ineffective use of memory and may lead to controller reboot.", fields: [{ name: "_", unit: "#", type: "u16", storage: 2, isSimpleType: true }], optional: true, packFormat: "u16" }, { kind: "rw", name: "max_power", identifier: 7, description: "Limit the power drawn by the light-strip (and controller).", fields: [{ name: "_", unit: "mA", type: "u16", storage: 2, isSimpleType: true, defaultValue: 200 }], identifierName: "max_power", packFormat: "u16" }, { kind: "const", name: "max_pixels", identifier: 385, description: "The maximum supported number of pixels.\nAll writes to `num_pixels` are clamped to `max_pixels`.", fields: [{ name: "_", unit: "#", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }, { kind: "rw", name: "num_repeats", identifier: 130, description: "How many times to repeat the program passed in `run` command.\nShould be set before the `run` command.\nSetting to `0` means to repeat forever.", fields: [{ name: "_", unit: "#", type: "u16", storage: 2, isSimpleType: true, defaultValue: 1 }], packFormat: "u16" }, { kind: "const", name: "variant", identifier: 263, description: "Specifies the shape of the light strip.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }, { kind: "command", name: "run", identifier: 129, description: 'Run the given light "program". See service description for details.', fields: [{ name: "program", type: "bytes", storage: 0, isSimpleType: true }], unique: true, packFormat: "b" }], tags: ["C"], group: "Light" }, { name: "Light bulb", status: "rc", shortId: "lightbulb", camelName: "lightBulb", shortName: "lightBulb", extends: ["_base"], notes: { short: "A light bulb controller." }, classIdentifier: 480970060, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "brightness", identifier: 1, description: "Indicates the brightness of the light bulb. Zero means completely off and 0xffff means completely on.\nFor non-dimmable lights, the value should be clamp to 0xffff for any non-zero value.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], identifierName: "intensity", packFormat: "u0.16" }, { kind: "const", name: "dimmable", identifier: 384, description: "Indicates if the light supports dimming.", fields: [{ name: "_", type: "bool", storage: 1 }], optional: true, packFormat: "u8" }], tags: [], group: "Light" }, { name: "Light level", status: "stable", shortId: "lightlevel", camelName: "lightLevel", shortName: "lightLevel", extends: ["_base", "_sensor"], notes: { short: "A sensor that measures luminosity level." }, classIdentifier: 400333340, enums: { Variant: { name: "Variant", storage: 1, members: { PhotoResistor: 1, ReverseBiasedLED: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "light_level", identifier: 257, description: "Detect light level", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, identifierName: "reading", packFormat: "u0.16" }, { kind: "ro", name: "light_level_error", identifier: 262, description: "Absolute estimated error of the reading value", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u0.16" }, { kind: "const", name: "variant", identifier: 263, description: "The type of physical sensor.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["8bit", "padauk", "input"], group: "Environment" }, { name: "Logger", status: "stable", shortId: "logger", camelName: "logger", shortName: "logger", extends: ["_base"], notes: { short: "A service which can report messages to the bus." }, classIdentifier: 316415946, enums: { Priority: { name: "Priority", storage: 1, members: { Debug: 0, Log: 1, Warning: 2, Error: 3, Silent: 4 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "min_priority", identifier: 128, description: "Messages with level lower than this won't be emitted. The default setting may vary.\nLoggers should revert this to their default setting if the register has not been\nupdated in 3000ms, and also keep the lowest setting they have seen in the last 1500ms.\nThus, clients should write this register every 1000ms and ignore messages which are\ntoo verbose for them.", fields: [{ name: "_", type: "Priority", storage: 1, defaultValue: 1 }], packFormat: "u8" }, { kind: "report", name: "debug", identifier: 128, description: "Report a message.", fields: [{ name: "message", type: "string", storage: 0 }], packFormat: "s" }, { kind: "report", name: "log", identifier: 129, description: "Report a message.", fields: [{ name: "message", type: "string", storage: 0 }], packFormat: "s" }, { kind: "report", name: "warn", identifier: 130, description: "Report a message.", fields: [{ name: "message", type: "string", storage: 0 }], packFormat: "s" }, { kind: "report", name: "error", identifier: 131, description: "Report a message.", fields: [{ name: "message", type: "string", storage: 0 }], packFormat: "s" }], tags: ["infrastructure", "C"] }, { name: "Magnetic field level", status: "stable", shortId: "magneticfieldlevel", camelName: "magneticFieldLevel", shortName: "magneticFieldLevel", extends: ["_base", "_sensor"], notes: { short: "A sensor that measures strength and polarity of magnetic field." }, classIdentifier: 318642191, enums: { Variant: { name: "Variant", storage: 1, members: { AnalogNS: 1, AnalogN: 2, AnalogS: 3, DigitalNS: 4, DigitalN: 5, DigitalS: 6 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "strength", identifier: 257, description: "Indicates the strength of magnetic field between -1 and 1.\nWhen no magnet is present the value should be around 0.\nFor analog sensors,\nwhen the north pole of the magnet is on top of the module\nand closer than south pole, then the value should be positive.\nFor digital sensors,\nthe value should either `0` or `1`, regardless of polarity.", fields: [{ name: "_", unit: "/", shift: 15, type: "i1.15", storage: -2 }], volatile: true, identifierName: "reading", packFormat: "i1.15" }, { kind: "ro", name: "detected", identifier: 385, description: "Determines if the magnetic field is present.\nIf the event `active` is observed, `detected` is true; if `inactive` is observed, `detected` is false.", fields: [{ name: "_", type: "bool", storage: 1 }], client: true, packFormat: "u8" }, { kind: "const", name: "variant", identifier: 263, description: "Determines which magnetic poles the sensor can detected,\nand whether or not it can measure their strength or just presence.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }, { kind: "event", name: "active", identifier: 1, description: "Emitted when strong-enough magnetic field is detected.", fields: [], identifierName: "active" }, { kind: "event", name: "inactive", identifier: 2, description: "Emitted when strong-enough magnetic field is no longer detected.", fields: [], identifierName: "inactive" }], tags: ["8bit", "padauk", "input"], group: "Environment" }, { name: "Magnetometer", status: "rc", shortId: "magnetomer", camelName: "magnetometer", shortName: "magnetometer", extends: ["_base", "_sensor"], notes: { short: "A 3-axis magnetometer." }, classIdentifier: 318935176, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "forces", identifier: 257, description: "Indicates the current magnetic field on magnetometer.\nFor reference: `1 mgauss` is `100 nT` (and `1 gauss` is `100 000 nT`).", fields: [{ name: "x", unit: "nT", type: "i32", storage: -4, isSimpleType: true }, { name: "y", unit: "nT", type: "i32", storage: -4, isSimpleType: true }, { name: "z", unit: "nT", type: "i32", storage: -4, isSimpleType: true }], volatile: true, identifierName: "reading", packFormat: "i32 i32 i32" }, { kind: "ro", name: "forces_error", identifier: 262, description: "Absolute estimated error on the readings.", fields: [{ name: "_", unit: "nT", type: "i32", storage: -4, isSimpleType: true }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "i32" }, { kind: "command", name: "calibrate", identifier: 2, description: "Forces a calibration sequence where the user/device\nmight have to rotate to be calibrated.", fields: [], identifierName: "calibrate" }], tags: [] }, { name: "Matrix Keypad", status: "experimental", shortId: "matrixkeypad", camelName: "matrixKeypad", shortName: "matrixKeypad", extends: ["_base", "_sensor"], notes: { short: "A matrix of buttons connected as a keypad" }, classIdentifier: 319172040, enums: { Variant: { name: "Variant", storage: 1, members: { Membrane: 1, Keyboard: 2, Elastomer: 3, ElastomerLEDPixel: 4 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "pressed", identifier: 257, description: "The coordinate of the button currently pressed. Keys are zero-indexed from left to right, top to bottom:\n``row = index / columns``, ``column = index % columns``.", fields: [{ name: "index", type: "u8", storage: 1, isSimpleType: true, startRepeats: true }], volatile: true, identifierName: "reading", packFormat: "r: u8" }, { kind: "const", name: "rows", identifier: 384, description: "Number of rows in the matrix", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "const", name: "columns", identifier: 385, description: "Number of columns in the matrix", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "const", name: "labels", identifier: 386, description: "The characters printed on the keys if any, in indexing sequence.", fields: [{ name: "label", type: "string0", storage: 0, startRepeats: true }], optional: true, packFormat: "r: z" }, { kind: "const", name: "variant", identifier: 263, description: "The type of physical keypad. If the variant is ``ElastomerLEDPixel``\nand the next service on the device is a ``LEDPixel`` service, it is considered\nas the service controlling the LED pixel on the keypad.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }, { kind: "event", name: "down", identifier: 1, description: "Emitted when a key, at the given index, goes from inactive (`pressed == 0`) to active.", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true }], identifierName: "active", packFormat: "u8" }, { kind: "event", name: "up", identifier: 2, description: "Emitted when a key, at the given index, goes from active (`pressed == 1`) to inactive.", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true }], identifierName: "inactive", packFormat: "u8" }, { kind: "event", name: "click", identifier: 128, description: "Emitted together with `up` when the press time was not longer than 500ms.", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "event", name: "long_click", identifier: 129, description: "Emitted together with `up` when the press time was more than 500ms.", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }], tags: [], group: "Button" }, { name: "Microphone", status: "experimental", shortId: "microphone", camelName: "microphone", shortName: "microphone", extends: ["_base"], notes: { short: "A single-channel microphone." }, classIdentifier: 289254534, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "sample", identifier: 129, description: "The samples will be streamed back over the `samples` pipe.\nIf `num_samples` is `0`, streaming will only stop when the pipe is closed.\nOtherwise the specified number of samples is streamed.\nSamples are sent as `i16`.", fields: [{ name: "samples", type: "pipe", storage: 12 }, { name: "num_samples", type: "u32", storage: 4, isSimpleType: true }], pipeType: "sample", packFormat: "b[12] u32" }, { kind: "rw", name: "sampling_period", identifier: 128, description: "Get or set microphone sampling period.\nSampling rate is `1_000_000 / sampling_period Hz`.", fields: [{ name: "_", unit: "us", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }], tags: [], group: "Sound" }, { name: "MIDI output", status: "experimental", shortId: "midioutput", camelName: "midiOutput", shortName: "midiOutput", extends: ["_base"], notes: { short: "A MIDI output device." }, classIdentifier: 444894423, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "enabled", identifier: 1, description: "Opens or closes the port to the MIDI device", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "command", name: "clear", identifier: 128, description: "Clears any pending send data that has not yet been sent from the MIDIOutput's queue.", fields: [] }, { kind: "command", name: "send", identifier: 129, description: "Enqueues the message to be sent to the corresponding MIDI port", fields: [{ name: "data", type: "bytes", storage: 0, isSimpleType: true }], unique: true, packFormat: "b" }], tags: [], group: "Sound" }, { name: "Model Runner", status: "experimental", shortId: "modelrunner", camelName: "modelRunner", shortName: "modelRunner", extends: ["_base"], notes: { short: "Runs machine learning models.\n\nOnly models with a single input tensor and a single output tensor are supported at the moment.\nInput is provided by Sensor Aggregator service on the same device.\nMultiple instances of this service may be present, if more than one model format is supported by a device." }, classIdentifier: 336566904, enums: { ModelFormat: { name: "ModelFormat", storage: 4, members: { TFLite: 860636756, ML4F: 809963362, EdgeImpulseCompiled: 810961221 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "set_model", identifier: 128, description: "Open pipe for streaming in the model. The size of the model has to be declared upfront.\nThe model is streamed over regular pipe data packets.\nThe format supported by this instance of the service is specified in `format` register.\nWhen the pipe is closed, the model is written all into flash, and the device running the service may reset.", fields: [{ name: "model_size", unit: "B", type: "u32", storage: 4, isSimpleType: true }], unique: true, hasReport: true, packFormat: "u32" }, { kind: "report", name: "set_model", identifier: 128, description: "Open pipe for streaming in the model. The size of the model has to be declared upfront.\nThe model is streamed over regular pipe data packets.\nThe format supported by this instance of the service is specified in `format` register.\nWhen the pipe is closed, the model is written all into flash, and the device running the service may reset.", fields: [{ name: "model_port", type: "pipe_port", storage: 2 }], secondary: true, pipeType: "set_model", packFormat: "u16" }, { kind: "command", name: "predict", identifier: 129, description: "Open channel that can be used to manually invoke the model. When enough data is sent over the `inputs` pipe, the model is invoked,\nand results are send over the `outputs` pipe.", fields: [{ name: "outputs", type: "pipe", storage: 12 }], pipeType: "predict", hasReport: true, packFormat: "b[12]" }, { kind: "report", name: "predict", identifier: 129, description: "Open channel that can be used to manually invoke the model. When enough data is sent over the `inputs` pipe, the model is invoked,\nand results are send over the `outputs` pipe.", fields: [{ name: "inputs", type: "pipe_port", storage: 2 }], secondary: true, pipeType: "predict", packFormat: "u16" }, { kind: "rw", name: "auto_invoke_every", identifier: 128, description: "When register contains `N > 0`, run the model automatically every time new `N` samples are collected.\nModel may be run less often if it takes longer to run than `N * sampling_interval`.\nThe `outputs` register will stream its value after each run.\nThis register is not stored in flash.", fields: [{ name: "_", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }, { kind: "ro", name: "outputs", identifier: 257, description: "Results of last model invocation as `float32` array.", fields: [{ name: "output", isFloat: true, type: "f32", storage: 4, startRepeats: true }], volatile: true, identifierName: "reading", packFormat: "r: f32" }, { kind: "ro", name: "input_shape", identifier: 384, description: "The shape of the input tensor.", fields: [{ name: "dimension", type: "u16", storage: 2, isSimpleType: true, startRepeats: true }], packFormat: "r: u16" }, { kind: "ro", name: "output_shape", identifier: 385, description: "The shape of the output tensor.", fields: [{ name: "dimension", type: "u16", storage: 2, isSimpleType: true, startRepeats: true }], packFormat: "r: u16" }, { kind: "ro", name: "last_run_time", identifier: 386, description: "The time consumed in last model execution.", fields: [{ name: "_", unit: "us", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "ro", name: "allocated_arena_size", identifier: 387, description: "Number of RAM bytes allocated for model execution.", fields: [{ name: "_", unit: "B", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "ro", name: "model_size", identifier: 388, description: "The size of the model in bytes.", fields: [{ name: "_", unit: "B", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "ro", name: "last_error", identifier: 389, description: "Textual description of last error when running or loading model (if any).", fields: [{ name: "_", type: "string", storage: 0 }], packFormat: "s" }, { kind: "const", name: "format", identifier: 390, description: "The type of ML models supported by this service.\n`TFLite` is flatbuffer `.tflite` file.\n`ML4F` is compiled machine code model for Cortex-M4F.\nThe format is typically present as first or second little endian word of model file.", fields: [{ name: "_", type: "ModelFormat", storage: 4 }], packFormat: "u32" }, { kind: "const", name: "format_version", identifier: 391, description: "A version number for the format.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "const", name: "parallel", identifier: 392, description: "If present and true this service can run models independently of other\ninstances of this service on the device.", fields: [{ name: "_", type: "bool", storage: 1 }], optional: true, packFormat: "u8" }], tags: [] }, { name: "Motion", status: "experimental", shortId: "motion", camelName: "motion", shortName: "motion", extends: ["_base", "_sensor"], notes: { short: "A sensor, typically PIR, that detects object motion within a certain range" }, classIdentifier: 293185353, enums: { Variant: { name: "Variant", storage: 1, members: { PIR: 1 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "moving", identifier: 257, description: "Reports is movement is currently detected by the sensor.", fields: [{ name: "_", type: "bool", storage: 1 }], volatile: true, identifierName: "reading", preferredInterval: 1e3, packFormat: "u8" }, { kind: "const", name: "max_distance", identifier: 384, description: "Maximum distance where objects can be detected.", fields: [{ name: "_", unit: "m", shift: 16, type: "u16.16", storage: 4 }], optional: true, packFormat: "u16.16" }, { kind: "const", name: "angle", identifier: 385, description: "Opening of the field of view", fields: [{ name: "_", unit: "\xB0", type: "u16", storage: 2, isSimpleType: true }], optional: true, packFormat: "u16" }, { kind: "const", name: "variant", identifier: 263, description: "Type of physical sensor", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }, { kind: "event", name: "movement", identifier: 1, description: "A movement was detected.", fields: [], identifierName: "active" }], tags: ["8bit"], group: "Movement" }, { name: "Motor", status: "rc", shortId: "motor", camelName: "motor", shortName: "motor", extends: ["_base"], notes: { short: "A DC motor." }, classIdentifier: 385895640, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "speed", identifier: 2, description: "Relative speed of the motor. Use positive/negative values to run the motor forwards and backwards.\nPositive is recommended to be clockwise rotation and negative counterclockwise. A speed of ``0`` \nwhile ``enabled`` acts as brake.", fields: [{ name: "_", unit: "/", shift: 15, type: "i1.15", storage: -2 }], identifierName: "value", packFormat: "i1.15" }, { kind: "rw", name: "enabled", identifier: 1, description: "Turn the power to the motor on/off.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "const", name: "load_torque", identifier: 384, description: "Torque required to produce the rated power of an electrical motor at load speed.", fields: [{ name: "_", unit: "kg/cm", shift: 16, type: "u16.16", storage: 4 }], optional: true, packFormat: "u16.16" }, { kind: "const", name: "load_rotation_speed", identifier: 385, description: "Revolutions per minute of the motor under full load.", fields: [{ name: "_", unit: "rpm", shift: 16, type: "u16.16", storage: 4 }], optional: true, packFormat: "u16.16" }, { kind: "const", name: "reversible", identifier: 386, description: "Indicates if the motor can run backwards.", fields: [{ name: "_", type: "bool", storage: 1 }], optional: true, packFormat: "u8" }], tags: ["C", "8bit"] }, { name: "Multitouch", status: "experimental", shortId: "multitouch", camelName: "multitouch", shortName: "multitouch", extends: ["_base", "_sensor"], notes: { short: "A capacitive touch sensor with multiple inputs.", events: "Most events include the channel number of the input." }, classIdentifier: 487664309, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "capacity", identifier: 257, description: "Capacitance of channels. The capacitance is continuously calibrated, and a value of `0` indicates\nno touch, wheres a value of around `100` or more indicates touch.\nIt's best to ignore this (unless debugging), and use events.\n\nDigital sensors will use `0` or `0xffff` on each channel.", fields: [{ name: "capacitance", type: "i16", storage: -2, isSimpleType: true, startRepeats: true }], volatile: true, identifierName: "reading", packFormat: "r: i16" }, { kind: "event", name: "touch", identifier: 1, description: "Emitted when an input is touched.", fields: [{ name: "channel", type: "u8", storage: 1, isSimpleType: true }], identifierName: "active", packFormat: "u8" }, { kind: "event", name: "release", identifier: 2, description: "Emitted when an input is no longer touched.", fields: [{ name: "channel", type: "u8", storage: 1, isSimpleType: true }], identifierName: "inactive", packFormat: "u8" }, { kind: "event", name: "tap", identifier: 128, description: "Emitted when an input is briefly touched. TODO Not implemented.", fields: [{ name: "channel", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "event", name: "long_press", identifier: 129, description: "Emitted when an input is touched for longer than 500ms. TODO Not implemented.", fields: [{ name: "channel", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "event", name: "swipe_pos", identifier: 144, description: "Emitted when input channels are successively touched in order of increasing channel numbers.", fields: [{ name: "duration", unit: "ms", type: "u16", storage: 2, isSimpleType: true }, { name: "start_channel", type: "u8", storage: 1, isSimpleType: true }, { name: "end_channel", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u16 u8 u8" }, { kind: "event", name: "swipe_neg", identifier: 145, description: "Emitted when input channels are successively touched in order of decreasing channel numbers.", fields: [{ name: "duration", unit: "ms", type: "u16", storage: 2, isSimpleType: true }, { name: "start_channel", type: "u8", storage: 1, isSimpleType: true }, { name: "end_channel", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u16 u8 u8" }], tags: [], group: "Button" }, { name: "PC controller", status: "experimental", shortId: "pccontroller", camelName: "PCController", shortName: "PCController", extends: ["_base"], notes: { short: "Send various events to PC, including opening a URL, start an app, sending text, etc." }, classIdentifier: 289212807, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "open_url", identifier: 128, description: "Open a URL in the default browser.", fields: [{ name: "url", type: "string", storage: 0 }], packFormat: "s" }, { kind: "command", name: "start_app", identifier: 129, description: "Start an app.", fields: [{ name: "name", type: "string", storage: 0 }], packFormat: "s" }, { kind: "command", name: "send_text", identifier: 130, description: "Send text to the active window.", fields: [{ name: "text", type: "string", storage: 0 }], packFormat: "s" }, { kind: "command", name: "run_script", identifier: 131, description: "Run a script.", fields: [{ name: "script", type: "string", storage: 0 }], packFormat: "s" }], tags: [] }, { name: "PC monitor", status: "experimental", shortId: "pcmonitor", camelName: "PCMonitor", shortName: "PCMonitor", extends: ["_base"], notes: { short: "Measures PC monitor." }, classIdentifier: 409107221, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "ro", name: "cpu_usage", identifier: 400, description: "CPU usage in percent.", fields: [{ name: "_", unit: "%", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "ro", name: "cpu_temperature", identifier: 401, description: "CPU temperature in Celsius.", fields: [{ name: "_", unit: "\xB0C", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "ro", name: "ram_usage", identifier: 402, description: "RAM usage in percent.", fields: [{ name: "_", unit: "%", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "ro", name: "gpu_information", identifier: 403, description: "GPU info.", fields: [{ name: "usage", unit: "%", type: "u8", storage: 1, isSimpleType: true }, { name: "temperature", unit: "\xB0C", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8 u8" }, { kind: "ro", name: "network_information", identifier: 405, description: "Network transmit/receive speed in Kbytes per second.\n\nA measure of PC monitor.", fields: [{ name: "tx", unit: "KB", type: "u16", storage: 2, isSimpleType: true }, { name: "rx", unit: "KB", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16 u16" }], tags: [] }, { name: "Planar position", status: "experimental", shortId: "planarposition", camelName: "planarPosition", shortName: "planarPosition", extends: ["_base", "_sensor"], notes: { short: "A sensor that repors 2D position, typically an optical mouse tracking sensor.\n\nThe sensor starts at an arbitrary origin (0,0) and reports the distance from that origin.\n\nThe `streaming_interval` is respected when the position is changing. When the position is not changing, the streaming interval may be throttled to `500ms`." }, classIdentifier: 499351381, enums: { Variant: { name: "Variant", storage: 1, members: { OpticalMousePosition: 1 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "position", identifier: 257, description: "The current position of the sensor.", fields: [{ name: "x", unit: "mm", shift: 10, type: "i22.10", storage: -4 }, { name: "y", unit: "mm", shift: 10, type: "i22.10", storage: -4 }], volatile: true, identifierName: "reading", packFormat: "i22.10 i22.10" }, { kind: "const", name: "variant", identifier: 263, description: "Specifies the type of physical sensor.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: [], group: "Movement" }, { name: "Potentiometer", status: "stable", shortId: "potentiometer", camelName: "potentiometer", shortName: "potentiometer", extends: ["_base", "_sensor"], notes: { short: "A slider or rotary potentiometer." }, classIdentifier: 522667846, enums: { Variant: { name: "Variant", storage: 1, members: { Slider: 1, Rotary: 2, Hall: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "position", identifier: 257, description: "The relative position of the slider.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, identifierName: "reading", packFormat: "u0.16" }, { kind: "const", name: "variant", identifier: 263, description: "Specifies the physical layout of the potentiometer.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["C", "8bit", "input"], group: "Slider" }, { name: "Power", status: "experimental", shortId: "power", camelName: "power", shortName: "power", extends: ["_base"], notes: { short: "A power-provider service.", long: "## Power negotiation protocol\n\nThe purpose of the power negotiation is to ensure that there is no more than [Iout_hc(max)](https://microsoft.github.io/jacdac-docs/reference/electrical-spec/#power-providers) delivered to the power rail.\nThis is realized by limiting the number of enabled power provider services to one.\n\nNote, that it's also possible to have low-current power providers,\nwhich are limited to [Iout_lc(max)](https://microsoft.github.io/jacdac-docs/reference/electrical-spec/#power-providers) and do not run a power provider service.\nThese are **not** accounted for in the power negotiation protocol.\n\nPower providers can have multiple _channels_, typically multiple Jacdac ports on the provider.\nEach channel can be limited to [Iout_hc(max)](https://microsoft.github.io/jacdac-docs/reference/electrical-spec/#power-providers) separately.\nIn normal operation, the data lines of each channels are connected together.\nThe ground lines are always connected together.\nMulti-channel power providers are also called _powered hubs_.\n\nWhile channels have separate current limits, there's nothing to prevent the user\nfrom joining two or more channels outside of the provider using a passive hub.\nThis would allow more than [Iout_hc(max)](https://microsoft.github.io/jacdac-docs/reference/electrical-spec/#power-providers) of current to be drawn, resulting in cables or components\ngetting hot and/or malfunctioning.\nThus, the power negotiation protocol seeks to detect situations where\nmultiple channels of power provider(s) are bridged together\nand shut down all but one of the channels involved.\n\nThe protocol is built around the power providers periodically sending specially crafted\n`shutdown` commands in broadcast mode.\nNote that this is unusual - services typically only send reports.\n\nThe `shutdown` commands can be reliably identified based on their first half (more details below).\nWhen a power provider starts receiving a `shutdown` command, it needs to take\nsteps to identify which of its channels the command is coming from.\nThis is typically realized with analog switches between the data lines of channels.\nThe delivery of power over the channel which received the `shutdown` command is then shut down.\nNote that in the case of a single-channel provider, any received `shutdown` command will cause a shut down.\n\nA multi-channel provider needs to also identify when a `shutdown` command it sent from one channel\nis received on any of its other channels and shut down one of the channels involved.\n\nIt is also possible to build a _data bridge_ device, with two or more ports.\nIt passes through all data except for `shutdown` commands,\nbut **does not** connect the power lines.\n\n### Protocol details\n\nThe `shutdown` commands follow a very narrow format:\n* they need to be the only packet in the frame (and thus we can also call them `shutdown` frames)\n* the second word of `device_id` needs to be set to `0xAA_AA_AA_AA` (alternating 0 and 1)\n* the service index is set to `0x3d`\n* the CRC is therefore fixed\n* therefore, the packet can be recognized by reading the first 8 bytes (total length is 16 bytes)\n\nThe exact byte structure of `shutdown` command is:\n`15 59 04 05 5A C9 A4 1F AA AA AA AA 00 3D 80 00`\n\nThere is one power service per channel.\nA multi-channel power provider can be implemented as one device with multiple services (typically with one MCU),\nor many devices with one service each (typically multiple MCUs).\nThe first option is preferred as it fixes the order of channels,\nbut the second option may be cheaper to implement.\n\nAfter queuing a `shutdown` command, the service enters a grace period\nuntil the report has been sent on the wire.\nDuring the grace period incoming `shutdown` commands are ignored.\n\n* Upon reset, a power service enables itself, and then only after 0-300ms (random)\n sends the first `shutdown` command\n* Every enabled power service emits `shutdown` commands every 400-600ms (random; first few packets can be even sent more often)\n* If an enabled power service sees a `shutdown` command from somebody else,\n it disables itself (unless in grace period)\n* If a disabled power service sees no `shutdown` command for more than ~1200ms, it enables itself\n (this is when the previous power source is unplugged or otherwise malfunctions)\n* If a power service has been disabled for around 10s, it enables itself.\n\nAdditionally:\n* While the `allowed` register is set to `0`, the service will not enable itself (nor send `shutdown` commands)\n* When a current overdraw is detected, the service stop providing power and enters `Overload` state for around 2 seconds,\n while still sending `shutdown` commands.\n\n### Client notes\n\nIf a client hears a `shutdown` command it just means it's on a branch of the\nnetwork with a (high) power provider.\nAs clients (brains) typically do not consume much current (as opposed to, say, servos),\nthe `shutdown` commands are typically irrelevant to them.\n\nFor power monitoring, the `power_status_changed` event (and possibly `power_status` register)\ncan be used.\nIn particular, user interfaces may alert the user to `Overload` status.\nThe `Overprovision` status is generally considered normal (eg. when two multi-channel power providers are linked together).\n\n### Server implementation notes\n\n#### A dedicated MCU per channel\n\nEvery channel has:\n* a cheap 8-bit MCU (e.g. PMS150C, PFS122),\n* a current limiter with FAULT output and ENABLE input, and\n* an analog switch.\n\nThe MCU here needs at least 4 pins per channel.\nIt is connected to data line of the channel, the control input of the switch, and to the current limiter's FAULT and ENABLE pins.\n\nThe switch connects the data line of the channel (JD_DATA_CHx) with the internal shared data bus, common to all channels (JD_DATA_COM).\nBoth the switch and the limiter are controlled by the MCU.\nA latching circuit is not needed for the limiter because the MCU will detect an overcurrent fault and shut it down immediately. \n\nDuring reception, after the beginning of `shutdown` frame is detected,\nthe switch is opened for a brief period.\nIf the `shutdown` frame is received correctly, it means it was on MCU's channel.\n\nIn the case of only one power delivery channel that's controlled by a dedicated MCU, the analog switch is not necessary. \n\n#### A shared MCU for multiple channels\n\nEvery channel has:\n* a current limiter with FAULT output and ENABLE input, \n* an analog switch, and\n* a wiggle-detection line connecting the MCU to data line of the channel\n\nThe MCU again needs at least 4 pins per channel.\nSwitches and limiters are set up like in the configuration above.\nThe Jacdac data line of the MCU is connected to internal data bus.\n\nWhile a Jacdac packet is being received, the MCU keeps checking if it is a \nbeginning of the `shutdown` frame.\nIf that is the case, it opens all switches and checks which one(s) of the channel\ndata lines wiggle (via the wiggle lines; this can be done with EXTI latches).\nThe one(s) that wiggle received the `shutdown` frame and need to be disabled.\n\nAlso, while sending the `shutdown` frames itself, it needs to be done separately\nfor each channel, with all the other switches open.\nIf during that operation we detect wiggle on other channels, then we have detected\na loop, and the respective channels needs to be disabled.\n\n#### A brain-integrated power supply\n\nHere, there's only one channel of power and we don't have hard real time requirements,\nso user-programmable brain can control it.\nThere is no need for analog switch or wiggle-detection line,\nbut a current limiter with a latching circuit is needed.\n\nThere is nothing special to do during reception of `shutdown` packet.\nWhen it is received, the current limiter should just be disabled.\n\nIdeally, exception/hard-fault handlers on the MCU should also disable the current limiter.\nSimilarly, the limiter should be disabled while the MCU is in bootloader mode,\nor otherwise unaware of the power negotiation protocol. \n\n### Rationale for the grace period\n\nConsider the following scenario:\n\n* device A queues `shutdown` command for sending\n* A receives external `shutdown` packet from B (thus disabling A)\n* the A `shutdown` command is sent from the queue (thus eventually disabling B)\n\nTo avoid that, we make sure that at the precise instant when `shutdown` command is sent,\nthe power is enabled (and thus will stay enabled until another `shutdown` command arrives).\nThis could be achieved by inspecting the enable bit, aftering acquiring the line\nand before starting UART transmission, however that would require breaking abstraction layers.\nSo instead, we never disable the service, while the `shutdown` packet is queued.\nThis may lead to delays in disabling power services, but these should be limited due to the\nrandom nature of the `shutdown` packet spacing.\n\n### Rationale for timings\n\nThe initial 0-300ms delay is set to spread out the `shutdown` periods of power services,\nto minimize collisions.\nThe `shutdown` periods are randomized 400-600ms, instead of a fixed 500ms used for regular\nservices, for the same reason.\n\nThe 1200ms period is set so that droping two `shutdown` packets in a row\nfrom the current provider will not cause power switch, while missing 3 will.\n\nThe 50-60s power switch period is arbitrary, but chosen to limit amount of switching between supplies,\nwhile keeping it short enough for user to notice any problems such switching may cause." }, classIdentifier: 530893146, enums: { PowerStatus: { name: "PowerStatus", storage: 1, members: { Disallowed: 0, Powering: 1, Overload: 2, Overprovision: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "allowed", identifier: 1, description: "Can be used to completely disable the service.\nWhen allowed, the service may still not be providing power, see \n`power_status` for the actual current state.", fields: [{ name: "_", type: "bool", storage: 1, defaultValue: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "rw", name: "max_power", identifier: 7, description: "Limit the power provided by the service. The actual maximum limit will depend on hardware.\nThis field may be read-only in some implementations - you should read it back after setting.", fields: [{ name: "_", unit: "mA", type: "u16", storage: 2, isSimpleType: true, defaultValue: 900, typicalMax: 900, typicalMin: 0 }], optional: true, identifierName: "max_power", packFormat: "u16" }, { kind: "ro", name: "power_status", identifier: 385, description: "Indicates whether the power provider is currently providing power (`Powering` state), and if not, why not.\n`Overprovision` means there was another power provider, and we stopped not to overprovision the bus.", fields: [{ name: "_", type: "PowerStatus", storage: 1 }], volatile: true, packFormat: "u8" }, { kind: "ro", name: "current_draw", identifier: 257, description: "Present current draw from the bus.", fields: [{ name: "_", unit: "mA", type: "u16", storage: 2, isSimpleType: true }], volatile: true, optional: true, identifierName: "reading", packFormat: "u16" }, { kind: "ro", name: "battery_voltage", identifier: 384, description: "Voltage on input.", fields: [{ name: "_", unit: "mV", type: "u16", storage: 2, isSimpleType: true, typicalMin: 4500, typicalMax: 5500 }], volatile: true, optional: true, packFormat: "u16" }, { kind: "ro", name: "battery_charge", identifier: 386, description: "Fraction of charge in the battery.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, optional: true, packFormat: "u0.16" }, { kind: "const", name: "battery_capacity", identifier: 387, description: "Energy that can be delivered to the bus when battery is fully charged.\nThis excludes conversion overheads if any.", fields: [{ name: "_", unit: "mWh", type: "u32", storage: 4, isSimpleType: true }], optional: true, packFormat: "u32" }, { kind: "rw", name: "keep_on_pulse_duration", identifier: 128, description: "Many USB power packs need current to be drawn from time to time to prevent shutdown.\nThis regulates how often and for how long such current is drawn.\nTypically a 1/8W 22 ohm resistor is used as load. This limits the duty cycle to 10%.", fields: [{ name: "_", unit: "ms", type: "u16", storage: 2, isSimpleType: true, defaultValue: 600 }], optional: true, packFormat: "u16" }, { kind: "rw", name: "keep_on_pulse_period", identifier: 129, description: "Many USB power packs need current to be drawn from time to time to prevent shutdown.\nThis regulates how often and for how long such current is drawn.\nTypically a 1/8W 22 ohm resistor is used as load. This limits the duty cycle to 10%.", fields: [{ name: "_", unit: "ms", type: "u16", storage: 2, isSimpleType: true, defaultValue: 2e4 }], optional: true, packFormat: "u16" }, { kind: "command", name: "shutdown", identifier: 128, description: "Sent by the power service periodically, as broadcast.", fields: [] }, { kind: "event", name: "power_status_changed", identifier: 3, description: "Emitted whenever `power_status` changes.", fields: [{ name: "power_status", type: "PowerStatus", storage: 1 }], identifierName: "change", packFormat: "u8" }], tags: [] }, { name: "Power supply", status: "experimental", shortId: "powersupply", camelName: "powerSupply", shortName: "powerSupply", extends: ["_base"], notes: { short: "A power supply with a fixed or variable voltage range" }, classIdentifier: 524302175, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "enabled", identifier: 1, description: "Turns the power supply on with `true`, off with `false`.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "rw", name: "output_voltage", identifier: 2, description: "The current output voltage of the power supply. Values provided must be in the range `minimum_voltage` to `maximum_voltage`", fields: [{ name: "_", unit: "V", isFloat: true, type: "f64", storage: 8 }], identifierName: "value", packFormat: "f64" }, { kind: "const", name: "minimum_voltage", identifier: 272, description: "The minimum output voltage of the power supply. For fixed power supplies, `minimum_voltage` should be equal to `maximum_voltage`.", fields: [{ name: "_", unit: "V", isFloat: true, type: "f64", storage: 8 }], identifierName: "min_value", packFormat: "f64" }, { kind: "const", name: "maximum_voltage", identifier: 273, description: "The maximum output voltage of the power supply. For fixed power supplies, `minimum_voltage` should be equal to `maximum_voltage`.", fields: [{ name: "_", unit: "V", isFloat: true, type: "f64", storage: 8 }], identifierName: "max_value", packFormat: "f64" }], tags: [] }, { name: "Pressure Button", status: "rc", shortId: "pressurebutton", camelName: "pressureButton", shortName: "pressureButton", extends: ["_base"], notes: { short: "A pressure sensitive push-button." }, classIdentifier: 672612547, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "threshold", identifier: 6, description: "Indicates the threshold for ``up`` events.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], identifierName: "active_threshold", packFormat: "u0.16" }], tags: ["8bit"], group: "Button" }, { name: "Protocol Test", status: "experimental", shortId: "prototest", camelName: "protoTest", shortName: "protoTest", extends: ["_base"], notes: { short: "This is test service to validate the protocol packet transmissions between the browser and a MCU.\nUse this page if you are porting Jacdac to a new platform.", long: "### Test procedure\n\nFor each ``rw`` registers, set a random value ``x``\n * read ``rw`` and check value is equal to ``x``\n * read ``ro`` and check value is equal to ``x``\n * listen to ``e`` event and check that data is equal to ``x``\n * call ``c`` command with new random value ``y``\n * read ``rw`` and check value is equal to ``y``\n * do all the above steps with acks\n\nFor each ``rw`` registers, there shall also\nbe an ``event`` and a ``command``. The event\nshould get raised when the value changes;\nand the command should set the value.", registers: "Every ``rw`` register has a corresponding ``ro`` regisrer\nand a corresponding ``set_...`` command to also set the value." }, classIdentifier: 382158442, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "rw_bool", identifier: 129, description: "A read write bool register.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "ro", name: "ro_bool", identifier: 385, description: "A read only bool register. Mirrors rw_bool.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "rw", name: "rw_u32", identifier: 130, description: "A read write u32 register.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "ro", name: "ro_u32", identifier: 386, description: "A read only u32 register.. Mirrors rw_u32.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "rw", name: "rw_i32", identifier: 131, description: "A read write i32 register.", fields: [{ name: "_", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "ro", name: "ro_i32", identifier: 387, description: "A read only i32 register.. Mirrors rw_i32.", fields: [{ name: "_", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "rw", name: "rw_string", identifier: 132, description: "A read write string register.", fields: [{ name: "_", type: "string", storage: 0 }], packFormat: "s" }, { kind: "ro", name: "ro_string", identifier: 388, description: "A read only string register. Mirrors rw_string.", fields: [{ name: "_", type: "string", storage: 0 }], packFormat: "s" }, { kind: "rw", name: "rw_bytes", identifier: 133, description: "A read write string register.", fields: [{ name: "_", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "b" }, { kind: "ro", name: "ro_bytes", identifier: 389, description: "A read only string register. Mirrors ro_bytes.", fields: [{ name: "_", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "b" }, { kind: "rw", name: "rw_i8_u8_u16_i32", identifier: 134, description: "A read write i8, u8, u16, i32 register.", fields: [{ name: "i8", type: "i8", storage: -1, isSimpleType: true }, { name: "u8", type: "u8", storage: 1, isSimpleType: true }, { name: "u16", type: "u16", storage: 2, isSimpleType: true }, { name: "i32", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i8 u8 u16 i32" }, { kind: "ro", name: "ro_i8_u8_u16_i32", identifier: 390, description: "A read only i8, u8, u16, i32 register.. Mirrors rw_i8_u8_u16_i32.", fields: [{ name: "i8", type: "i8", storage: -1, isSimpleType: true }, { name: "u8", type: "u8", storage: 1, isSimpleType: true }, { name: "u16", type: "u16", storage: 2, isSimpleType: true }, { name: "i32", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i8 u8 u16 i32" }, { kind: "rw", name: "rw_u8_string", identifier: 135, description: "A read write u8, string register.", fields: [{ name: "u8", type: "u8", storage: 1, isSimpleType: true }, { name: "str", type: "string", storage: 0 }], packFormat: "u8 s" }, { kind: "ro", name: "ro_u8_string", identifier: 391, description: "A read only u8, string register.. Mirrors rw_u8_string.", fields: [{ name: "u8", type: "u8", storage: 1, isSimpleType: true }, { name: "str", type: "string", storage: 0 }], packFormat: "u8 s" }, { kind: "event", name: "e_bool", identifier: 129, description: "An event raised when rw_bool is modified", fields: [{ name: "bo", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "event", name: "e_u32", identifier: 130, description: "An event raised when rw_u32 is modified", fields: [{ name: "u32", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "event", name: "e_i32", identifier: 131, description: "An event raised when rw_i32 is modified", fields: [{ name: "i32", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "event", name: "e_string", identifier: 132, description: "An event raised when rw_string is modified", fields: [{ name: "str", type: "string", storage: 0 }], packFormat: "s" }, { kind: "event", name: "e_bytes", identifier: 133, description: "An event raised when rw_bytes is modified", fields: [{ name: "bytes", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "b" }, { kind: "event", name: "e_i8_u8_u16_i32", identifier: 134, description: "An event raised when rw_i8_u8_u16_i32 is modified", fields: [{ name: "i8", type: "i8", storage: -1, isSimpleType: true }, { name: "u8", type: "u8", storage: 1, isSimpleType: true }, { name: "u16", type: "u16", storage: 2, isSimpleType: true }, { name: "i32", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i8 u8 u16 i32" }, { kind: "event", name: "e_u8_string", identifier: 135, description: "An event raised when rw_u8_string is modified", fields: [{ name: "u8", type: "u8", storage: 1, isSimpleType: true }, { name: "str", type: "string", storage: 0 }], packFormat: "u8 s" }, { kind: "command", name: "c_bool", identifier: 129, description: "A command to set rw_bool.", fields: [{ name: "bo", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "command", name: "c_u32", identifier: 130, description: "A command to set rw_u32.", fields: [{ name: "u32", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "command", name: "c_i32", identifier: 131, description: "A command to set rw_i32.", fields: [{ name: "i32", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i32" }, { kind: "command", name: "c_string", identifier: 132, description: "A command to set rw_string.", fields: [{ name: "str", type: "string", storage: 0 }], packFormat: "s" }, { kind: "command", name: "c_bytes", identifier: 133, description: "A command to set rw_string.", fields: [{ name: "bytes", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "b" }, { kind: "command", name: "c_i8_u8_u16_i32", identifier: 134, description: "A command to set rw_bytes.", fields: [{ name: "i8", type: "i8", storage: -1, isSimpleType: true }, { name: "u8", type: "u8", storage: 1, isSimpleType: true }, { name: "u16", type: "u16", storage: 2, isSimpleType: true }, { name: "i32", type: "i32", storage: -4, isSimpleType: true }], packFormat: "i8 u8 u16 i32" }, { kind: "command", name: "c_u8_string", identifier: 135, description: "A command to set rw_u8_string.", fields: [{ name: "u8", type: "u8", storage: 1, isSimpleType: true }, { name: "str", type: "string", storage: 0 }], packFormat: "u8 s" }, { kind: "command", name: "c_report_pipe", identifier: 144, description: "A command to read the content of rw_bytes, byte per byte, as a pipe.", fields: [{ name: "p_bytes", type: "pipe", storage: 12 }], pipeType: "c_report_pipe", packFormat: "b[12]" }, { kind: "pipe_report", name: "p_bytes", identifier: 0, description: "A command to read the content of rw_bytes, byte per byte, as a pipe.", fields: [{ name: "byte", type: "u8", storage: 1, isSimpleType: true }], pipeType: "c_report_pipe", packFormat: "u8" }], tags: ["infrastructure"] }, { name: "Proxy", status: "stable", shortId: "proxy", camelName: "proxy", shortName: "proxy", extends: ["_base"], notes: { short: "A service that tags a device as a packet proxy.\nA device in proxy mode will proxy Jacdac packets and typically has its core logic halted." }, classIdentifier: 384932169, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }], tags: ["infrastructure"] }, { name: "Pulse Oximeter", status: "experimental", shortId: "pulseoximeter", camelName: "pulseOximeter", shortName: "pulseOximeter", extends: ["_base", "_sensor"], notes: { short: "A sensor approximating the oxygen level.\n\n**Jacdac is not suitable for medical devices and should NOT be used in any kind of device to diagnose or treat any medical conditions.**" }, classIdentifier: 280710838, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "oxygen", identifier: 257, description: "The estimated oxygen level in blood.", fields: [{ name: "_", unit: "%", shift: 8, type: "u8.8", storage: 2, typicalMin: 80, typicalMax: 100 }], volatile: true, identifierName: "reading", packFormat: "u8.8" }, { kind: "ro", name: "oxygen_error", identifier: 262, description: "The estimated error on the reported sensor data.", fields: [{ name: "_", unit: "%", shift: 8, type: "u8.8", storage: 2 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u8.8" }], tags: ["8bit"], group: "Biometric" }, { name: "Rain gauge", status: "rc", shortId: "raingauge", camelName: "rainGauge", shortName: "rainGauge", extends: ["_base", "_sensor"], notes: { short: "Measures the amount of liquid precipitation over an area in a predefined period of time." }, classIdentifier: 326323349, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "precipitation", identifier: 257, description: "Total precipitation recorded so far.", fields: [{ name: "_", unit: "mm", shift: 16, type: "u16.16", storage: 4 }], volatile: true, identifierName: "reading", preferredInterval: 6e4, packFormat: "u16.16" }, { kind: "const", name: "precipitation_precision", identifier: 264, description: "Typically the amount of rain needed for tipping the bucket.", fields: [{ name: "_", unit: "mm", shift: 16, type: "u16.16", storage: 4 }], optional: true, identifierName: "reading_resolution", packFormat: "u16.16" }], tags: ["8bit"], group: "Environment" }, { name: "Real time clock", status: "rc", shortId: "realtimeclock", camelName: "realTimeClock", shortName: "realTimeClock", extends: ["_base", "_sensor"], notes: { short: "Real time clock to support collecting data with precise time stamps." }, classIdentifier: 445323816, enums: { Variant: { name: "Variant", storage: 1, members: { Computer: 1, Crystal: 2, Cuckoo: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "local_time", identifier: 257, description: "Current time in 24h representation. Default streaming period is 1 second.\n\n- `day_of_month` is day of the month, starting at `1`\n- `day_of_week` is day of the week, starting at `1` as monday. Leave at 0 if unsupported.", fields: [{ name: "year", type: "u16", storage: 2, isSimpleType: true }, { name: "month", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 1, absoluteMax: 12 }, { name: "day_of_month", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 0, absoluteMax: 31 }, { name: "day_of_week", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 1, absoluteMax: 7 }, { name: "hour", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 0, absoluteMax: 23 }, { name: "min", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 0, absoluteMax: 59 }, { name: "sec", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 0, absoluteMax: 60 }], volatile: true, identifierName: "reading", preferredInterval: 1e3, packFormat: "u16 u8 u8 u8 u8 u8 u8" }, { kind: "ro", name: "drift", identifier: 384, description: "Time drift since the last call to the `set_time` command.", fields: [{ name: "_", unit: "s", shift: 16, type: "u16.16", storage: 4 }], volatile: true, optional: true, packFormat: "u16.16" }, { kind: "const", name: "precision", identifier: 385, description: "Error on the clock, in parts per million of seconds.", fields: [{ name: "_", unit: "ppm", shift: 16, type: "u16.16", storage: 4 }], optional: true, packFormat: "u16.16" }, { kind: "const", name: "variant", identifier: 263, description: "The type of physical clock used by the sensor.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }, { kind: "command", name: "set_time", identifier: 128, description: "Sets the current time and resets the error.", fields: [{ name: "year", type: "u16", storage: 2, isSimpleType: true }, { name: "month", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 1, absoluteMax: 12 }, { name: "day_of_month", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 1, absoluteMax: 31 }, { name: "day_of_week", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 1, absoluteMax: 7 }, { name: "hour", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 0, absoluteMax: 23 }, { name: "min", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 0, absoluteMax: 59 }, { name: "sec", type: "u8", storage: 1, isSimpleType: true, absoluteMin: 0, absoluteMax: 60 }], packFormat: "u16 u8 u8 u8 u8 u8 u8" }], tags: ["8bit"] }, { name: "Reflected light", status: "rc", shortId: "reflectedlight", camelName: "reflectedLight", shortName: "reflectedLight", extends: ["_base", "_sensor"], notes: { short: "A sensor that detects light and dark surfaces, commonly used for line following robots." }, classIdentifier: 309087410, enums: { Variant: { name: "Variant", storage: 1, members: { InfraredDigital: 1, InfraredAnalog: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "brightness", identifier: 257, description: "Reports the reflected brightness. It may be a digital value or, for some sensor, analog value.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, identifierName: "reading", packFormat: "u0.16" }, { kind: "const", name: "variant", identifier: 263, description: "Type of physical sensor used", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["8bit"], group: "Environment" }, { name: "Relay", status: "stable", shortId: "relay", camelName: "relay", shortName: "relay", extends: ["_base"], notes: { short: "A switching relay.\n\nThe contacts should be labelled `NO` (normally open), `COM` (common), and `NC` (normally closed).\nWhen relay is energized it connects `NO` and `COM`.\nWhen relay is not energized it connects `NC` and `COM`.\nSome relays may be missing `NO` or `NC` contacts.\nWhen relay module is not powered, or is in bootloader mode, it is not energized (connects `NC` and `COM`)." }, classIdentifier: 406840918, enums: { Variant: { name: "Variant", storage: 1, members: { Electromechanical: 1, SolidState: 2, Reed: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "active", identifier: 1, description: "Indicates whether the relay circuit is currently energized or not.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "const", name: "variant", identifier: 263, description: "Describes the type of relay used.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }, { kind: "const", name: "max_switching_current", identifier: 384, description: "Maximum switching current for a resistive load.", fields: [{ name: "_", unit: "mA", type: "u32", storage: 4, isSimpleType: true }], optional: true, packFormat: "u32" }], tags: ["8bit"] }, { name: "Random Number Generator", status: "rc", shortId: "rng", camelName: "rng", shortName: "rng", extends: ["_base"], notes: { short: "Generates random numbers using entropy sourced from physical processes.\n\nThis typically uses a cryptographical pseudo-random number generator (for example [Fortuna]()),\nwhich is periodically re-seeded with entropy coming from some hardware source." }, classIdentifier: 394916002, enums: { Variant: { name: "Variant", storage: 1, members: { Quantum: 1, ADCNoise: 2, WebCrypto: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "ro", name: "random", identifier: 384, description: "A register that returns a 64 bytes random buffer on every request.\nThis never blocks for a long time. If you need additional random bytes, keep querying the register.", fields: [{ name: "_", type: "bytes", storage: 0, isSimpleType: true }], volatile: true, packFormat: "b" }, { kind: "const", name: "variant", identifier: 263, description: "The type of algorithm/technique used to generate the number.\n`Quantum` refers to dedicated hardware device generating random noise due to quantum effects.\n`ADCNoise` is the noise from quick readings of analog-digital converter, which reads temperature of the MCU or some floating pin.\n`WebCrypto` refers is used in simulators, where the source of randomness comes from an advanced operating system.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: [] }, { name: "Role Manager", status: "experimental", shortId: "rolemanager", camelName: "roleManager", shortName: "roleManager", extends: ["_base"], notes: { short: "Assign roles to services on the Jacdac bus.", long: "## Role allocation\n\nInternally, the role manager stores a mapping from role name to `(device_id, service_idx)`.\nUsers refer to services by using role names (eg., they instantiate an accelerometer client with a given role name).\nEach client has a role, and roles are unique to clients\n(ie., one should not have both a gyro and accelerometer service with role `left_leg`).\n\nThe simplest recommended automatic role assignment algorithm is as follows:\n\n```text\nroles.sort(strcmp() on UTF-8 representation of role name)\ndevices.sort(by device identifier (lexicographic on 8 byte string))\nmove self device to beginning of devices list\nfor every role\n if role is not assigned\n for every device\n for every service on device\n if serviceClass matches role\n if service is not assigned to any role\n assign service to role\n```\n\nDue to sorting, role names sharing a prefix will tend to be co-located on the single device.\nFor example, one can have roles `left_leg_acc`, `left_leg_gyro`, `right_leg_acc`, `right_leg_gyro`,\nand assuming combined gyro+accelerometer sensors, the pairs will tend to be allocated to a single leg,\nhowever the legs may be reversed.\nIn such a case the user can swap the physical sensors (note that left leg will always be assigned to\nsensor with smaller device identifier).\nAlternatively, the user can manually modify assignment using `set_role` command." }, classIdentifier: 508264038, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "auto_bind", identifier: 128, description: 'Normally, if some roles are unfilled, and there are idle services that can fulfill them,\nthe brain device will assign roles (bind) automatically.\nSuch automatic assignment happens every second or so, and is trying to be smart about\nco-locating roles that share "host" (part before first slash),\nas well as reasonably stable assignments.\nOnce user start assigning roles manually using this service, auto-binding should be disabled to avoid confusion.', fields: [{ name: "_", type: "bool", storage: 1, defaultValue: 1 }], packFormat: "u8" }, { kind: "ro", name: "all_roles_allocated", identifier: 385, description: "Indicates if all required roles have been allocated to devices.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "command", name: "set_role", identifier: 129, description: "Set role. Can set to empty to remove role binding.", fields: [{ name: "device_id", type: "devid", storage: 8 }, { name: "service_idx", type: "u8", storage: 1, isSimpleType: true }, { name: "role", type: "string", storage: 0 }], packFormat: "b[8] u8 s" }, { kind: "command", name: "clear_all_roles", identifier: 132, description: "Remove all role bindings.", fields: [] }, { kind: "command", name: "list_roles", identifier: 131, description: "List all roles and bindings required by the current program. `device_id` and `service_idx` are `0` if role is unbound.", fields: [{ name: "roles", type: "pipe", storage: 12 }], pipeType: "list_roles", packFormat: "b[12]" }, { kind: "pipe_report", name: "roles", identifier: 0, description: "List all roles and bindings required by the current program. `device_id` and `service_idx` are `0` if role is unbound.", fields: [{ name: "device_id", type: "devid", storage: 8 }, { name: "service_class", type: "u32", storage: 4, isSimpleType: true }, { name: "service_idx", type: "u8", storage: 1, isSimpleType: true }, { name: "role", type: "string", storage: 0 }], pipeType: "list_roles", packFormat: "b[8] u32 u8 s" }, { kind: "event", name: "change", identifier: 3, description: "Notifies that role bindings have changed.", fields: [], identifierName: "change" }], tags: ["infrastructure"] }, { name: "ROS", status: "experimental", shortId: "ros", camelName: "ros", shortName: "ros", extends: ["_base"], notes: { short: "A ROS (Robot Operating System https://www.ros.org/) controller that can act as a broker for messages." }, classIdentifier: 354743340, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "publish_message", identifier: 129, description: "Publishes a JSON-encoded message to the given topic.", fields: [{ name: "node", type: "string0", storage: 0 }, { name: "topic", type: "string0", storage: 0 }, { name: "message", type: "string", storage: 0 }], packFormat: "z z s" }, { kind: "command", name: "subscribe_message", identifier: 130, description: "Subscribes to a message topic. Subscribed topics will emit message reports.", fields: [{ name: "node", type: "string0", storage: 0 }, { name: "topic", type: "string", storage: 0 }], packFormat: "z s" }, { kind: "report", name: "message", identifier: 131, description: "A message published on the bus. Message is JSON encoded.", fields: [{ name: "node", type: "string0", storage: 0 }, { name: "topic", type: "string0", storage: 0 }, { name: "message", type: "string", storage: 0 }], packFormat: "z z s" }], tags: ["robotics"] }, { name: "Rotary encoder", status: "stable", shortId: "rotaryencoder", camelName: "rotaryEncoder", shortName: "rotaryEncoder", extends: ["_base", "_sensor"], notes: { short: "An incremental rotary encoder - converts angular motion of a shaft to digital signal." }, classIdentifier: 284830153, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "position", identifier: 257, description: 'Upon device reset starts at `0` (regardless of the shaft position).\nIncreases by `1` for a clockwise "click", by `-1` for counter-clockwise.', fields: [{ name: "_", unit: "#", type: "i32", storage: -4, isSimpleType: true }], volatile: true, identifierName: "reading", packFormat: "i32" }, { kind: "const", name: "clicks_per_turn", identifier: 384, description: "This specifies by how much `position` changes when the crank does 360 degree turn. Typically 12 or 24.", fields: [{ name: "_", unit: "#", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }, { kind: "const", name: "clicker", identifier: 385, description: "The encoder is combined with a clicker. If this is the case, the clicker button service\nshould follow this service in the service list of the device.", fields: [{ name: "_", type: "bool", storage: 1 }], optional: true, packFormat: "u8" }], tags: ["C", "8bit", "input"], group: "Slider" }, { name: "Rover", status: "experimental", shortId: "rover", camelName: "rover", shortName: "rover", extends: ["_base", "_sensor"], notes: { short: "A roving robot." }, classIdentifier: 435474539, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "kinematics", identifier: 257, description: "The current position and orientation of the robot.", fields: [{ name: "x", unit: "cm", shift: 16, type: "i16.16", storage: -4 }, { name: "y", unit: "cm", shift: 16, type: "i16.16", storage: -4 }, { name: "vx", unit: "cm/s", shift: 16, type: "i16.16", storage: -4 }, { name: "vy", unit: "cm/s", shift: 16, type: "i16.16", storage: -4 }, { name: "heading", unit: "\xB0", shift: 16, type: "i16.16", storage: -4 }], volatile: true, identifierName: "reading", packFormat: "i16.16 i16.16 i16.16 i16.16 i16.16" }], tags: [] }, { name: "Satellite Navigation System", status: "experimental", shortId: "satelittenavigationsystem", camelName: "satNav", shortName: "satNav", extends: ["_base", "_sensor"], notes: { short: "A satellite-based navigation system like GPS, Gallileo, ..." }, classIdentifier: 433938742, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "position", identifier: 257, description: "Reported coordinates, geometric altitude and time of position. Altitude accuracy is 0 if not available.", fields: [{ name: "timestamp", unit: "ms", type: "u64", storage: 8, isSimpleType: true }, { name: "latitude", unit: "lat", shift: 23, type: "i9.23", storage: -4, absoluteMin: -90, absoluteMax: 90 }, { name: "longitude", unit: "lon", shift: 23, type: "i9.23", storage: -4, absoluteMin: -180, absoluteMax: 180 }, { name: "accuracy", unit: "m", shift: 16, type: "u16.16", storage: 4 }, { name: "altitude", unit: "m", shift: 6, type: "i26.6", storage: -4 }, { name: "altitudeAccuracy", unit: "m", shift: 16, type: "u16.16", storage: 4 }], volatile: true, identifierName: "reading", packFormat: "u64 i9.23 i9.23 u16.16 i26.6 u16.16" }, { kind: "rw", name: "enabled", identifier: 1, description: "Enables or disables the GPS module", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "event", name: "inactive", identifier: 2, description: "The module is disabled or lost connection with satellites.", fields: [], identifierName: "inactive" }], tags: [] }, { name: "Sensor Aggregator", status: "experimental", shortId: "sensoraggregator", camelName: "sensorAggregator", shortName: "sensorAggregator", extends: ["_base"], notes: { short: "Aggregate data from multiple sensors into a single stream\n(often used as input to machine learning models on the same device, see model runner service)." }, classIdentifier: 496034245, enums: { SampleType: { name: "SampleType", storage: 1, members: { U8: 8, I8: 136, U16: 16, I16: 144, U32: 32, I32: 160 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "inputs", identifier: 128, description: "Set automatic input collection.\nThese settings are stored in flash.", fields: [{ name: "sampling_interval", unit: "ms", type: "u16", storage: 2, isSimpleType: true }, { name: "samples_in_window", type: "u16", storage: 2, isSimpleType: true }, { name: "reserved", type: "u32", storage: 4, isSimpleType: true }, { name: "device_id", type: "devid", storage: 8, startRepeats: true }, { name: "service_class", type: "u32", storage: 4, isSimpleType: true }, { name: "service_num", type: "u8", storage: 1, isSimpleType: true }, { name: "sample_size", unit: "B", type: "u8", storage: 1, isSimpleType: true }, { name: "sample_type", type: "SampleType", storage: 1 }, { name: "sample_shift", type: "i8", storage: -1, isSimpleType: true }], packFormat: "u16 u16 u32 r: b[8] u32 u8 u8 u8 i8" }, { kind: "ro", name: "num_samples", identifier: 384, description: "Number of input samples collected so far.", fields: [{ name: "_", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "ro", name: "sample_size", identifier: 385, description: "Size of a single sample.", fields: [{ name: "_", unit: "B", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "rw", name: "streaming_samples", identifier: 129, description: "When set to `N`, will stream `N` samples as `current_sample` reading.", fields: [{ name: "_", unit: "#", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }, { kind: "ro", name: "current_sample", identifier: 257, description: "Last collected sample.", fields: [{ name: "_", type: "bytes", storage: 0, isSimpleType: true }], volatile: true, identifierName: "reading", packFormat: "b" }], tags: [] }, { name: "Serial", status: "experimental", shortId: "serial", camelName: "serial", shortName: "serial", extends: ["_base"], notes: { short: "An asynchronous serial communication service capable of sending and receiving buffers of data.\nSettings default to 115200 baud 8N1." }, classIdentifier: 297461188, enums: { ParityType: { name: "ParityType", storage: 1, members: { None: 0, Even: 1, Odd: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "connected", identifier: 1, description: "Indicates if the serial connection is active.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "ro", name: "connection_name", identifier: 385, description: "User-friendly name of the connection.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, packFormat: "s" }, { kind: "rw", name: "baud_rate", identifier: 128, description: "A positive, non-zero value indicating the baud rate at which serial communication is be established.", fields: [{ name: "_", unit: "baud", type: "u32", storage: 4, isSimpleType: true, defaultValue: 115200 }], packFormat: "u32" }, { kind: "rw", name: "data_bits", identifier: 129, description: "The number of data bits per frame. Either 7 or 8.", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true, defaultValue: 8, absoluteMin: 7, absoluteMax: 8 }], packFormat: "u8" }, { kind: "rw", name: "stop_bits", identifier: 130, description: "The number of stop bits at the end of a frame. Either 1 or 2.", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true, defaultValue: 1, absoluteMin: 1, absoluteMax: 2 }], packFormat: "u8" }, { kind: "rw", name: "parity_mode", identifier: 131, description: "The parity mode.", fields: [{ name: "_", type: "ParityType", storage: 1, defaultValue: 0 }], packFormat: "u8" }, { kind: "rw", name: "buffer_size", identifier: 132, description: "A positive, non-zero value indicating the size of the read and write buffers that should be created.", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "command", name: "send", identifier: 128, description: "Send a buffer of data over the serial transport.", fields: [{ name: "data", type: "bytes", storage: 0, isSimpleType: true }], unique: true, packFormat: "b" }, { kind: "report", name: "received", identifier: 128, description: "Raised when a buffer of data is received.", fields: [{ name: "data", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "b" }], tags: ["io"] }, { name: "Servo", status: "stable", shortId: "servo", camelName: "servo", shortName: "servo", extends: ["_base"], notes: { short: "Servo is a small motor with arm that can be pointing at a specific direction.\nTypically a servo angle is between 0\xB0 and 180\xB0 where 90\xB0 is the middle resting position.\n\nThe `min_pulse/max_pulse` may be read-only if the servo is permanently affixed to its Jacdac controller." }, classIdentifier: 318542083, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "angle", identifier: 2, description: "Specifies the angle of the arm (request).", fields: [{ name: "_", unit: "\xB0", shift: 16, type: "i16.16", storage: -4, typicalMin: 0, typicalMax: 180 }], identifierName: "value", packFormat: "i16.16" }, { kind: "rw", name: "enabled", identifier: 1, description: "Turn the power to the servo on/off.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "rw", name: "offset", identifier: 129, description: "Correction applied to the angle to account for the servo arm drift.", fields: [{ name: "_", unit: "\xB0", shift: 16, type: "i16.16", storage: -4 }], packFormat: "i16.16" }, { kind: "const", name: "min_angle", identifier: 272, description: "Lowest angle that can be set, typically 0 \xB0.", fields: [{ name: "_", unit: "\xB0", shift: 16, type: "i16.16", storage: -4 }], identifierName: "min_value", packFormat: "i16.16" }, { kind: "rw", name: "min_pulse", identifier: 131, description: "The length of pulse corresponding to lowest angle.", fields: [{ name: "_", unit: "us", type: "u16", storage: 2, isSimpleType: true, defaultValue: 500 }], optional: true, packFormat: "u16" }, { kind: "const", name: "max_angle", identifier: 273, description: "Highest angle that can be set, typically 180\xB0.", fields: [{ name: "_", unit: "\xB0", shift: 16, type: "i16.16", storage: -4 }], identifierName: "max_value", packFormat: "i16.16" }, { kind: "rw", name: "max_pulse", identifier: 133, description: "The length of pulse corresponding to highest angle.", fields: [{ name: "_", unit: "us", type: "u16", storage: 2, isSimpleType: true, defaultValue: 2500 }], optional: true, packFormat: "u16" }, { kind: "const", name: "stall_torque", identifier: 384, description: "The servo motor will stop rotating when it is trying to move a `stall_torque` weight at a radial distance of `1.0` cm.", fields: [{ name: "_", unit: "kg/cm", shift: 16, type: "u16.16", storage: 4 }], optional: true, packFormat: "u16.16" }, { kind: "const", name: "response_speed", identifier: 385, description: "Time to move 60\xB0.", fields: [{ name: "_", unit: "s/60\xB0", shift: 16, type: "u16.16", storage: 4 }], optional: true, packFormat: "u16.16" }, { kind: "ro", name: "actual_angle", identifier: 257, description: "The current physical position of the arm, if the device has a way to sense the position.", fields: [{ name: "_", unit: "\xB0", shift: 16, type: "i16.16", storage: -4 }], volatile: true, optional: true, identifierName: "reading", packFormat: "i16.16" }], tags: ["C"] }, { name: "Settings", status: "experimental", shortId: "settings", camelName: "settings", shortName: "settings", extends: ["_base"], notes: { short: "Non-volatile key-value storage interface for storing settings.", long: "## Secrets\n\nEntries with keys starting with `$` are considered secret.\nThey can be written normally, but they read as a single `0` byte,\nunless they are empty, in which case the value returned is also empty.\nThese are typically used by other services on the same device." }, classIdentifier: 285727818, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "get", identifier: 128, description: "Get the value of given setting. If no such entry exists, the value returned is empty.", fields: [{ name: "key", type: "string", storage: 0 }], hasReport: true, packFormat: "s" }, { kind: "report", name: "get", identifier: 128, description: "Get the value of given setting. If no such entry exists, the value returned is empty.", fields: [{ name: "key", type: "string0", storage: 0 }, { name: "value", type: "bytes", storage: 0, isSimpleType: true }], secondary: true, packFormat: "z b" }, { kind: "command", name: "set", identifier: 129, description: "Set the value of a given setting.", fields: [{ name: "key", type: "string0", storage: 0 }, { name: "value", type: "bytes", storage: 0, isSimpleType: true }], restricted: true, packFormat: "z b" }, { kind: "command", name: "delete", identifier: 132, description: "Delete a given setting.", fields: [{ name: "key", type: "string", storage: 0 }], restricted: true, packFormat: "s" }, { kind: "command", name: "list_keys", identifier: 130, description: "Return keys of all settings.", fields: [{ name: "results", type: "pipe", storage: 12 }], pipeType: "list_keys", packFormat: "b[12]" }, { kind: "pipe_report", name: "listed_key", identifier: 0, description: "Return keys of all settings.", fields: [{ name: "key", type: "string", storage: 0 }], pipeType: "list_keys", packFormat: "s" }, { kind: "command", name: "list", identifier: 131, description: "Return keys and values of all settings.", fields: [{ name: "results", type: "pipe", storage: 12 }], pipeType: "list", packFormat: "b[12]" }, { kind: "pipe_report", name: "listed_entry", identifier: 0, description: "Return keys and values of all settings.", fields: [{ name: "key", type: "string0", storage: 0 }, { name: "value", type: "bytes", storage: 0, isSimpleType: true }], pipeType: "list", packFormat: "z b" }, { kind: "command", name: "clear", identifier: 133, description: "Clears all keys.", fields: [], restricted: true }, { kind: "event", name: "change", identifier: 3, description: "Notifies that some setting have been modified.", fields: [], identifierName: "change" }], tags: [] }, { name: "7-segment display", status: "rc", shortId: "sevensegmentdisplay", camelName: "sevenSegmentDisplay", shortName: "sevenSegmentDisplay", extends: ["_base"], notes: { short: "A 7-segment numeric display, with one or more digits." }, classIdentifier: 425810167, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "digits", identifier: 2, description: "Each byte encodes the display status of a digit using,\nwhere lowest bit 0 encodes segment `A`, bit 1 encodes segments `B`, ..., bit 6 encodes segments `G`, and bit 7 encodes the decimal point (if present).\nIf incoming `digits` data is smaller than `digit_count`, the remaining digits will be cleared.\nThus, sending an empty `digits` payload clears the screen.\n\n```text\nGFEDCBA DP\n - A -\n F B\n | |\n - G -\n | |\n E C _\n | | |DP|\n - D - -\n```", fields: [{ name: "_", encoding: "bitset", type: "bytes", storage: 0, isSimpleType: true }], lowLevel: true, identifierName: "value", packFormat: "b" }, { kind: "rw", name: "brightness", identifier: 1, description: "Controls the brightness of the LEDs. `0` means off.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], optional: true, identifierName: "intensity", packFormat: "u0.16" }, { kind: "rw", name: "double_dots", identifier: 128, description: "Turn on or off the column LEDs (separating minutes from hours, etc.) in of the segment.\nIf the column LEDs is not supported, the value remains false.", fields: [{ name: "_", type: "bool", storage: 1 }], optional: true, packFormat: "u8" }, { kind: "const", name: "digit_count", identifier: 384, description: "The number of digits available on the display.", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true }], packFormat: "u8" }, { kind: "const", name: "decimal_point", identifier: 385, description: "True if decimal points are available (on all digits).", fields: [{ name: "_", type: "bool", storage: 1 }], optional: true, packFormat: "u8" }, { kind: "command", name: "set_number", identifier: 128, description: "Shows the number on the screen using the decimal dot if available.", fields: [{ name: "value", isFloat: true, type: "f64", storage: 8 }], client: true, packFormat: "f64" }], tags: ["8bit"], group: "Display" }, { name: "Soil moisture", status: "stable", shortId: "soilmoisture", camelName: "soilMoisture", shortName: "soilMoisture", extends: ["_base", "_sensor"], notes: { short: "A soil moisture sensor." }, classIdentifier: 491430835, enums: { Variant: { name: "Variant", storage: 1, members: { Resistive: 1, Capacitive: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "moisture", identifier: 257, description: "Indicates the wetness of the soil, from `dry` to `wet`.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, identifierName: "reading", preferredInterval: 1e3, packFormat: "u0.16" }, { kind: "ro", name: "moisture_error", identifier: 262, description: "The error on the moisture reading.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u0.16" }, { kind: "const", name: "variant", identifier: 263, description: "Describe the type of physical sensor.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["8bit"], group: "Environment" }, { name: "Solenoid", status: "rc", shortId: "solenoid", camelName: "solenoid", shortName: "solenoid", extends: ["_base"], notes: { short: "A push-pull solenoid is a type of relay that pulls a coil when activated." }, classIdentifier: 387392458, enums: { Variant: { name: "Variant", storage: 1, members: { PushPull: 1, Valve: 2, Latch: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "pulled", identifier: 1, description: "Indicates whether the solenoid is energized and pulled (on) or pushed (off).", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "const", name: "variant", identifier: 263, description: "Describes the type of solenoid used.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["8bit"] }, { name: "Sound level", status: "rc", shortId: "soundlevel", camelName: "soundLevel", shortName: "soundLevel", extends: ["_base", "_sensor"], notes: { short: "A sound level detector sensor, gives a relative indication of the sound level." }, classIdentifier: 346888797, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "sound_level", identifier: 257, description: "The sound level detected by the microphone", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, identifierName: "reading", packFormat: "u0.16" }, { kind: "rw", name: "enabled", identifier: 1, description: "Turn on or off the microphone.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "rw", name: "loud_threshold", identifier: 6, description: "Set level at which the `loud` event is generated.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], identifierName: "active_threshold", packFormat: "u0.16" }, { kind: "rw", name: "quiet_threshold", identifier: 5, description: "Set level at which the `quiet` event is generated.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], identifierName: "inactive_threshold", packFormat: "u0.16" }, { kind: "event", name: "loud", identifier: 1, description: "Generated when a loud sound is detected.", fields: [], identifierName: "active" }, { kind: "event", name: "quiet", identifier: 2, description: "Generated low level of sound is detected.", fields: [], identifierName: "inactive" }], tags: ["8bit"], group: "Sound" }, { name: "Sound player", status: "rc", shortId: "soundplayer", camelName: "soundPlayer", shortName: "soundPlayer", extends: ["_base"], notes: { short: "A device that can play various sounds stored locally. This service is typically paired with a ``storage`` service for storing sounds." }, classIdentifier: 335795e3, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "volume", identifier: 1, description: "Global volume of the output. ``0`` means completely off. This volume is mixed with each play volumes.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], optional: true, identifierName: "intensity", packFormat: "u0.16" }, { kind: "command", name: "play", identifier: 128, description: "Starts playing a sound.", fields: [{ name: "name", type: "string", storage: 0 }], packFormat: "s" }, { kind: "command", name: "cancel", identifier: 129, description: "Cancel any sound playing.", fields: [] }, { kind: "command", name: "list_sounds", identifier: 130, description: "Returns the list of sounds available to play.", fields: [{ name: "sounds_port", type: "pipe", storage: 12 }], pipeType: "list_sounds", packFormat: "b[12]" }, { kind: "pipe_report", name: "list_sounds_pipe", identifier: 0, description: "Returns the list of sounds available to play.", fields: [{ name: "duration", unit: "ms", type: "u32", storage: 4, isSimpleType: true }, { name: "name", type: "string", storage: 0 }], pipeType: "list_sounds", packFormat: "u32 s" }], tags: [], group: "Sound" }, { name: "Sound Recorder with Playback", status: "experimental", shortId: "soundrecorderwithplayback", camelName: "soundRecorderWithPlayback", shortName: "soundRecorderWithPlayback", extends: ["_base"], notes: { short: "A record and replay module. You can record a few seconds of audio and play it back." }, classIdentifier: 460504912, enums: { Status: { name: "Status", storage: 1, members: { Idle: 0, Recording: 1, Playing: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "play", identifier: 128, description: "Replay recorded audio.", fields: [] }, { kind: "command", name: "record", identifier: 129, description: "Record audio for N milliseconds.", fields: [{ name: "duration", unit: "ms", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }, { kind: "command", name: "cancel", identifier: 130, description: "Cancel record, the `time` register will be updated by already cached data.", fields: [] }, { kind: "ro", name: "status", identifier: 384, description: "Indicate the current status", fields: [{ name: "_", type: "Status", storage: 1 }], packFormat: "u8" }, { kind: "ro", name: "time", identifier: 385, description: "Milliseconds of audio recorded.", fields: [{ name: "_", unit: "ms", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16" }, { kind: "rw", name: "volume", identifier: 1, description: "Playback volume control", fields: [{ name: "_", unit: "/", shift: 8, type: "u0.8", storage: 1 }], optional: true, identifierName: "intensity", packFormat: "u0.8" }], tags: ["8bit", "padauk"], group: "Sound" }, { name: "Sound Spectrum", status: "experimental", shortId: "soundspectrum", camelName: "soundSpectrum", shortName: "soundSpectrum", extends: ["_base", "_sensor"], notes: { short: "A microphone that analyzes the sound specturm" }, classIdentifier: 360365086, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "frequency_bins", identifier: 257, description: "The computed frequency data.", fields: [{ name: "_", type: "bytes", storage: 0, isSimpleType: true }], volatile: true, identifierName: "reading", packFormat: "b" }, { kind: "rw", name: "enabled", identifier: 1, description: "Turns on/off the micropohone.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "rw", name: "fft_pow2_size", identifier: 128, description: "The power of 2 used as the size of the FFT to be used to determine the frequency domain.", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true, defaultValue: 5, absoluteMin: 2, absoluteMax: 7 }], packFormat: "u8" }, { kind: "rw", name: "min_decibels", identifier: 129, description: "The minimum power value in the scaling range for the FFT analysis data", fields: [{ name: "_", unit: "dB", type: "i16", storage: -2, isSimpleType: true }], packFormat: "i16" }, { kind: "rw", name: "max_decibels", identifier: 130, description: "The maximum power value in the scaling range for the FFT analysis data", fields: [{ name: "_", unit: "dB", type: "i16", storage: -2, isSimpleType: true }], packFormat: "i16" }, { kind: "rw", name: "smoothing_time_constant", identifier: 131, description: 'The averaging constant with the last analysis frame.\nIf `0` is set, there is no averaging done, whereas a value of `1` means "overlap the previous and current buffer quite a lot while computing the value".', fields: [{ name: "_", unit: "/", shift: 8, type: "u0.8", storage: 1, defaultValue: 0.8 }], packFormat: "u0.8" }], tags: [], group: "Sound" }, { name: "Speech synthesis", status: "rc", shortId: "speechsynthesis", camelName: "speechSynthesis", shortName: "speechSynthesis", extends: ["_base"], notes: { short: "A speech synthesizer" }, classIdentifier: 302307733, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "enabled", identifier: 1, description: "Determines if the speech engine is in a non-paused state.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "rw", name: "lang", identifier: 128, description: "Language used for utterances as defined in https://www.ietf.org/rfc/bcp/bcp47.txt.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, packFormat: "s" }, { kind: "rw", name: "volume", identifier: 129, description: "Volume for utterances.", fields: [{ name: "_", unit: "/", shift: 8, type: "u0.8", storage: 1, defaultValue: 1 }], optional: true, packFormat: "u0.8" }, { kind: "rw", name: "pitch", identifier: 130, description: "Pitch for utterances", fields: [{ name: "_", shift: 16, type: "u16.16", storage: 4, defaultValue: 1, absoluteMax: 2, absoluteMin: 0 }], optional: true, packFormat: "u16.16" }, { kind: "rw", name: "rate", identifier: 131, description: "Rate for utterances", fields: [{ name: "_", shift: 16, type: "u16.16", storage: 4, defaultValue: 1, absoluteMin: 0.1, absoluteMax: 10 }], optional: true, packFormat: "u16.16" }, { kind: "command", name: "speak", identifier: 128, description: "Adds an utterance to the utterance queue; it will be spoken when any other utterances queued before it have been spoken.", fields: [{ name: "text", type: "string", storage: 0 }], unique: true, packFormat: "s" }, { kind: "command", name: "cancel", identifier: 129, description: "Cancels current utterance and all utterances from the utterance queue.", fields: [] }], tags: [] }, { name: "Switch", status: "rc", shortId: "switch", camelName: "switch", shortName: "switch", extends: ["_base", "_sensor"], notes: { short: "A switch, which keeps its position." }, classIdentifier: 450008066, enums: { Variant: { name: "Variant", storage: 1, members: { Slide: 1, Tilt: 2, PushButton: 3, Tactile: 4, Toggle: 5, Proximity: 6, Magnetic: 7, FootButton: 8 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "active", identifier: 257, description: "Indicates whether the switch is currently active (on).", fields: [{ name: "_", type: "bool", storage: 1 }], volatile: true, identifierName: "reading", packFormat: "u8" }, { kind: "const", name: "variant", identifier: 263, description: "Describes the type of switch used.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }, { kind: "event", name: "on", identifier: 1, description: "Emitted when switch goes from `off` to `on`.", fields: [], identifierName: "active" }, { kind: "event", name: "off", identifier: 2, description: "Emitted when switch goes from `on` to `off`.", fields: [], identifierName: "inactive" }], tags: ["8bit", "input"], group: "Button" }, { name: "TCP", status: "experimental", shortId: "tcp", camelName: "tcp", shortName: "tcp", extends: ["_base"], notes: { short: "Data transfer over TCP/IP and TLS stream sockets.", commands: "## Pipes" }, classIdentifier: 457422603, enums: { TcpError: { name: "TcpError", storage: -4, members: { InvalidCommand: 1, InvalidCommandPayload: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "open", identifier: 128, description: "Open pair of pipes between network peripheral and a controlling device. In/outbound refers to direction from/to internet.", fields: [{ name: "inbound", type: "pipe", storage: 12 }], pipeType: "open", hasReport: true, packFormat: "b[12]" }, { kind: "report", name: "open", identifier: 128, description: "Open pair of pipes between network peripheral and a controlling device. In/outbound refers to direction from/to internet.", fields: [{ name: "outbound_port", type: "pipe_port", storage: 2 }], secondary: true, pipeType: "open", packFormat: "u16" }, { kind: "meta_pipe_command", name: "open_ssl", identifier: 1, description: "Open an SSL connection to a given host:port pair. Can be issued only once on given pipe.\nAfter the connection is established, an empty data report is sent.\nConnection is closed by closing the pipe.", fields: [{ name: "tcp_port", type: "u16", storage: 2, isSimpleType: true }, { name: "hostname", type: "string", storage: 0 }], pipeType: "open", packFormat: "u16 s" }, { kind: "pipe_command", name: "outdata", identifier: 0, description: "Bytes to be sent directly over an established TCP or SSL connection.", fields: [{ name: "data", type: "bytes", storage: 0, isSimpleType: true }], pipeType: "open", packFormat: "b" }, { kind: "pipe_report", name: "indata", identifier: 0, description: "Bytes read directly from directly over an established TCP or SSL connection.", fields: [{ name: "data", type: "bytes", storage: 0, isSimpleType: true }], pipeType: "open", packFormat: "b" }, { kind: "meta_pipe_report", name: "error", identifier: 0, description: "Reported when an error is encountered. Negative error codes come directly from the SSL implementation.", fields: [{ name: "error", type: "TcpError", storage: -4 }], pipeType: "open", packFormat: "i32" }], tags: [] }, { name: "Temperature", status: "stable", shortId: "temperature", camelName: "temperature", shortName: "temperature", extends: ["_base", "_sensor"], notes: { short: "A thermometer measuring outside or inside environment." }, classIdentifier: 337754823, enums: { Variant: { name: "Variant", storage: 1, members: { Outdoor: 1, Indoor: 2, Body: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "temperature", identifier: 257, description: "The temperature.", fields: [{ name: "_", unit: "\xB0C", shift: 10, type: "i22.10", storage: -4 }], volatile: true, identifierName: "reading", preferredInterval: 1e3, packFormat: "i22.10" }, { kind: "const", name: "min_temperature", identifier: 260, description: "Lowest temperature that can be reported.", fields: [{ name: "_", unit: "\xB0C", shift: 10, type: "i22.10", storage: -4 }], identifierName: "min_reading", packFormat: "i22.10" }, { kind: "const", name: "max_temperature", identifier: 261, description: "Highest temperature that can be reported.", fields: [{ name: "_", unit: "\xB0C", shift: 10, type: "i22.10", storage: -4 }], identifierName: "max_reading", packFormat: "i22.10" }, { kind: "ro", name: "temperature_error", identifier: 262, description: "The real temperature is between `temperature - temperature_error` and `temperature + temperature_error`.", fields: [{ name: "_", unit: "\xB0C", shift: 10, type: "u22.10", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u22.10" }, { kind: "const", name: "variant", identifier: 263, description: "Specifies the type of thermometer.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["C", "8bit"], group: "Environment" }, { name: "Timeseries Aggregator", status: "experimental", shortId: "timeseriesaggregator", camelName: "timeseriesAggregator", shortName: "timeseriesAggregator", extends: ["_base"], notes: { short: "Supports aggregating timeseries data (especially sensor readings)\nand sending them to a cloud/storage service.\nUsed in DeviceScript.\n\nNote that `f64` values are not necessarily aligned." }, classIdentifier: 294829516, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "clear", identifier: 128, description: "Remove all pending timeseries.", fields: [] }, { kind: "command", name: "update", identifier: 131, description: "Add a data point to a timeseries.", fields: [{ name: "value", isFloat: true, type: "f64", storage: 8 }, { name: "label", type: "string", storage: 0 }], packFormat: "f64 s" }, { kind: "command", name: "set_window", identifier: 132, description: "Set aggregation window.\nSetting to `0` will restore default.", fields: [{ name: "duration", unit: "ms", type: "u32", storage: 4, isSimpleType: true }, { name: "label", type: "string", storage: 0 }], packFormat: "u32 s" }, { kind: "command", name: "set_upload", identifier: 133, description: "Set whether or not the timeseries will be uploaded to the cloud.\nThe `stored` reports are generated regardless.", fields: [{ name: "upload", type: "bool", storage: 1 }, { name: "label", type: "string", storage: 0 }], packFormat: "u8 s" }, { kind: "report", name: "stored", identifier: 144, description: 'Indicates that the average, minimum and maximum value of a given\ntimeseries are as indicated.\nIt also says how many samples were collected, and the collection period.\nTimestamps are given using device\'s internal clock, which will wrap around.\nTypically, `end_time` can be assumed to be "now".\n`end_time - start_time == window`', fields: [{ name: "num_samples", unit: "#", type: "u32", storage: 4, isSimpleType: true }, { name: "padding", type: "u8[4]", storage: 4 }, { name: "avg", isFloat: true, type: "f64", storage: 8 }, { name: "min", isFloat: true, type: "f64", storage: 8 }, { name: "max", isFloat: true, type: "f64", storage: 8 }, { name: "start_time", unit: "ms", type: "u32", storage: 4, isSimpleType: true }, { name: "end_time", unit: "ms", type: "u32", storage: 4, isSimpleType: true }, { name: "label", type: "string", storage: 0 }], packFormat: "u32 b[4] f64 f64 f64 u32 u32 s" }, { kind: "ro", name: "now", identifier: 384, description: "This can queried to establish local time on the device.", fields: [{ name: "_", unit: "us", type: "u32", storage: 4, isSimpleType: true }], volatile: true, packFormat: "u32" }, { kind: "rw", name: "fast_start", identifier: 128, description: "When `true`, the windows will be shorter after service reset and gradually extend to requested length.\nThis is ensure valid data is being streamed in program development.", fields: [{ name: "_", type: "bool", storage: 1, defaultValue: 1 }], packFormat: "u8" }, { kind: "rw", name: "default_window", identifier: 129, description: "Window for timeseries for which `set_window` was never called.\nNote that windows returned initially may be shorter if `fast_start` is enabled.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 6e4 }], packFormat: "u32" }, { kind: "rw", name: "default_upload", identifier: 130, description: "Whether labelled timeseries for which `set_upload` was never called should be automatically uploaded.", fields: [{ name: "_", type: "bool", storage: 1, defaultValue: 1 }], packFormat: "u8" }, { kind: "rw", name: "upload_unlabelled", identifier: 131, description: "Whether automatically created timeseries not bound in role manager should be uploaded.", fields: [{ name: "_", type: "bool", storage: 1, defaultValue: 1 }], packFormat: "u8" }, { kind: "rw", name: "sensor_watchdog_period", identifier: 132, description: "If no data is received from any sensor within given period, the device is rebooted.\nSet to `0` to disable (default).\nUpdating user-provided timeseries does not reset the watchdog.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], packFormat: "u32" }], tags: ["infrastructure", "devicescript"], group: "Iot", restricted: true }, { name: "Traffic Light", status: "rc", shortId: "trafficlight", camelName: "trafficLight", shortName: "trafficLight", extends: ["_base"], notes: { short: "Controls a mini traffic with a red, orange and green LED." }, classIdentifier: 365137307, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "red", identifier: 128, description: "The on/off state of the red light.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "rw", name: "yellow", identifier: 129, description: "The on/off state of the yellow light.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }, { kind: "rw", name: "green", identifier: 130, description: "The on/off state of the green light.", fields: [{ name: "_", type: "bool", storage: 1 }], packFormat: "u8" }], tags: ["8bit"] }, { name: "Total Volatile organic compound", status: "stable", shortId: "tvoc", camelName: "tvoc", shortName: "tvoc", extends: ["_base", "_sensor"], notes: { short: "Measures equivalent Total Volatile Organic Compound levels." }, classIdentifier: 312849815, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "TVOC", identifier: 257, description: "Total volatile organic compound readings in parts per billion.", fields: [{ name: "_", unit: "ppb", shift: 10, type: "u22.10", storage: 4, absoluteMin: 0, typicalMax: 1187, typicalMin: 0 }], volatile: true, identifierName: "reading", packFormat: "u22.10" }, { kind: "ro", name: "TVOC_error", identifier: 262, description: "Error on the reading data", fields: [{ name: "_", unit: "ppb", shift: 10, type: "u22.10", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u22.10" }, { kind: "const", name: "min_TVOC", identifier: 260, description: "Minimum measurable value", fields: [{ name: "_", unit: "ppb", shift: 10, type: "u22.10", storage: 4 }], identifierName: "min_reading", packFormat: "u22.10" }, { kind: "const", name: "max_TVOC", identifier: 261, description: "Minimum measurable value.", fields: [{ name: "_", unit: "ppb", shift: 10, type: "u22.10", storage: 4 }], identifierName: "max_reading", packFormat: "u22.10" }], tags: ["8bit"], group: "Environment" }, { name: "Unique Brain", status: "stable", shortId: "uniquebrain", camelName: "uniqueBrain", shortName: "uniqueBrain", extends: ["_base"], notes: { short: "The presence of this service indicates that this device is a client that controls sensors and actuators.\nIf a unique brain detects another younger unique brain (determined by reset counter in announce packets),\nthen the older brain should switch into proxy mode." }, classIdentifier: 272387813, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }], tags: ["infrastructure"] }, { name: "USB Bridge", status: "rc", shortId: "usbbridge", camelName: "usbBridge", shortName: "usbBridge", extends: ["_base"], notes: { short: "This service is normally not announced or otherwise exposed on the serial bus.\nIt is used to communicate with a USB-Jacdac bridge at the USB layer.\nThe host sends broadcast packets to this service to control the link layer.\nThe device responds with broadcast reports (no-one else does that).\nThese packets are not forwarded to the UART Jacdac line.\n\nPackets are sent over USB Serial (CDC).\nThe host shall set the CDC to 1Mbaud 8N1\n(even though in some cases the USB interface is connected directly to the MCU and line settings are\nignored).\n\nThe CDC line carries both Jacdac frames and serial logging output.\nJacdac frames have valid CRC and are framed by delimeter characters and possibly fragmented.\n\n`0xFE` is used as a framing byte.\nNote that bytes `0xF8`-`0xFF` are always invalid UTF-8.\n`0xFF` occurs relatively often in Jacdac packets, so is not used for framing.\n\nThe following sequences are supported:\n\n* `0xFE 0xF8` - literal 0xFE\n* `0xFE 0xF9` - reserved; ignore\n* `0xFE 0xFA` - indicates that some serial logs were dropped at this point\n* `0xFE 0xFB` - indicates that some Jacdac frames were dropped at this point\n* `0xFE 0xFC` - Jacdac frame start\n* `0xFE 0xFD` - Jacdac frame end\n\n0xFE followed by any other byte:\n* in serial, should be treated as literal 0xFE (and the byte as itself, unless it's 0xFE)\n* in frame data, should terminate the current frame fragment,\n and ideally have all data (incl. fragment start) in the current frame fragment treated as serial" }, classIdentifier: 418781770, enums: { QByte: { name: "QByte", storage: 1, members: { Magic: 254, LiteralMagic: 248, Reserved: 249, SerialGap: 250, FrameGap: 251, FrameStart: 252, FrameEnd: 253 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "disable_packets", identifier: 128, description: "Disables forwarding of Jacdac packets.", fields: [], hasReport: true }, { kind: "report", name: "disable_packets", identifier: 128, description: "Disables forwarding of Jacdac packets.", fields: [], secondary: true }, { kind: "command", name: "enable_packets", identifier: 129, description: "Enables forwarding of Jacdac packets.", fields: [], hasReport: true }, { kind: "report", name: "enable_packets", identifier: 129, description: "Enables forwarding of Jacdac packets.", fields: [], secondary: true }, { kind: "command", name: "disable_log", identifier: 130, description: "Disables forwarding of serial log messages.", fields: [], hasReport: true }, { kind: "report", name: "disable_log", identifier: 130, description: "Disables forwarding of serial log messages.", fields: [], secondary: true }, { kind: "command", name: "enable_log", identifier: 131, description: "Enables forwarding of serial log messages.", fields: [], hasReport: true }, { kind: "report", name: "enable_log", identifier: 131, description: "Enables forwarding of serial log messages.", fields: [], secondary: true }], tags: ["infrastructure"] }, { name: "UV index", status: "stable", shortId: "uvindex", camelName: "uvIndex", shortName: "uvIndex", extends: ["_base", "_sensor"], notes: { short: "The UV Index is a measure of the intensity of ultraviolet (UV) rays from the Sun." }, classIdentifier: 527306128, enums: { Variant: { name: "Variant", storage: 1, members: { UVA_UVB: 1, Visible_IR: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "uv_index", identifier: 257, description: "Ultraviolet index, typically refreshed every second.", fields: [{ name: "_", unit: "uv", shift: 16, type: "u16.16", storage: 4, typicalMax: 11, typicalMin: 0 }], volatile: true, identifierName: "reading", preferredInterval: 1e3, packFormat: "u16.16" }, { kind: "ro", name: "uv_index_error", identifier: 262, description: "Error on the UV measure.", fields: [{ name: "_", unit: "uv", shift: 16, type: "u16.16", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u16.16" }, { kind: "const", name: "variant", identifier: 263, description: "The type of physical sensor and capabilities.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["8bit"], group: "Environment" }, { name: "Verified Telemetry", status: "deprecated", shortId: "verifiedtelemetrysensor", camelName: "verifiedTelemetry", shortName: "verifiedTelemetry", extends: ["_base"], notes: { short: "A mixin service that exposes verified telemetry information for a sensor (see https://github.com/Azure/Verified-Telemetry/tree/main/PnPModel)." }, classIdentifier: 563381279, enums: { Status: { name: "Status", storage: 1, members: { Unknown: 0, Working: 1, Faulty: 2 } }, FingerprintType: { name: "FingerprintType", storage: 1, members: { FallCurve: 1, CurrentSense: 2, Custom: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "ro", name: "telemetry_status", identifier: 384, description: "Reads the telemetry working status, where ``true`` is working and ``false`` is faulty.", fields: [{ name: "_", type: "Status", storage: 1 }], packFormat: "u8" }, { kind: "rw", name: "telemetry_status_interval", identifier: 128, description: "Specifies the interval between computing the fingerprint information.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], optional: true, packFormat: "u32" }, { kind: "const", name: "fingerprint_type", identifier: 385, description: "Type of the fingerprint.", fields: [{ name: "_", type: "FingerprintType", storage: 1 }], packFormat: "u8" }, { kind: "ro", name: "fingerprint_template", identifier: 386, description: "Template Fingerprint information of a working sensor.", fields: [{ name: "confidence", unit: "%", type: "u16", storage: 2, isSimpleType: true }, { name: "template", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "u16 b" }, { kind: "command", name: "reset_fingerprint_template", identifier: 128, description: "This command will clear the template fingerprint of a sensor and collect a new template fingerprint of the attached sensor.", fields: [] }, { kind: "command", name: "retrain_fingerprint_template", identifier: 129, description: "This command will append a new template fingerprint to the `fingerprintTemplate`. Appending more fingerprints will increase the accuracy in detecting the telemetry status.", fields: [], unique: true }, { kind: "event", name: "telemetry_status_change", identifier: 3, description: "The telemetry status of the device was updated.", fields: [{ name: "telemetry_status", type: "Status", storage: 1 }], identifierName: "change", packFormat: "u8" }, { kind: "event", name: "fingerprint_template_change", identifier: 128, description: "The fingerprint template was updated", fields: [] }], tags: [] }, { name: "Vibration motor", status: "stable", shortId: "vibrationmotor", camelName: "vibrationMotor", shortName: "vibrationMotor", extends: ["_base"], notes: { short: "A vibration motor." }, classIdentifier: 406832290, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "vibrate", identifier: 128, description: "Starts a sequence of vibration and pauses. To stop any existing vibration, send an empty payload.", fields: [{ name: "duration", unit: "8ms", type: "u8", storage: 1, isSimpleType: true, startRepeats: true }, { name: "intensity", unit: "/", shift: 8, type: "u0.8", storage: 1 }], packFormat: "r: u8 u0.8" }, { kind: "const", name: "max_vibrations", identifier: 384, description: "The maximum number of vibration sequences supported in a single packet.", fields: [{ name: "_", type: "u8", storage: 1, isSimpleType: true }], optional: true, packFormat: "u8" }], tags: [] }, { name: "Water level", status: "rc", shortId: "waterlevel", camelName: "waterLevel", shortName: "waterLevel", extends: ["_base", "_sensor"], notes: { short: "A sensor that measures liquid/water level." }, classIdentifier: 343630573, enums: { Variant: { name: "Variant", storage: 1, members: { Resistive: 1, ContactPhotoElectric: 2, NonContactPhotoElectric: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "level", identifier: 257, description: "The reported water level.", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, identifierName: "reading", packFormat: "u0.16" }, { kind: "ro", name: "level_error", identifier: 262, description: "The error rage on the current reading", fields: [{ name: "_", unit: "/", shift: 16, type: "u0.16", storage: 2 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u0.16" }, { kind: "const", name: "variant", identifier: 263, description: "The type of physical sensor.", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }], tags: ["8bit"] }, { name: "Weight Scale", status: "rc", shortId: "weightscale", camelName: "weightScale", shortName: "weightScale", extends: ["_base", "_sensor"], notes: { short: "A weight measuring sensor." }, classIdentifier: 525160512, enums: { Variant: { name: "Variant", storage: 1, members: { Body: 1, Food: 2, Jewelry: 3 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "weight", identifier: 257, description: "The reported weight.", fields: [{ name: "_", unit: "kg", shift: 16, type: "u16.16", storage: 4 }], volatile: true, identifierName: "reading", packFormat: "u16.16" }, { kind: "ro", name: "weight_error", identifier: 262, description: "The estimate error on the reported reading.", fields: [{ name: "_", unit: "kg", shift: 16, type: "u16.16", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u16.16" }, { kind: "rw", name: "zero_offset", identifier: 128, description: "Calibrated zero offset error on the scale, i.e. the measured weight when nothing is on the scale.\nYou do not need to subtract that from the reading, it has already been done.", fields: [{ name: "_", unit: "kg", shift: 16, type: "u16.16", storage: 4 }], optional: true, packFormat: "u16.16" }, { kind: "rw", name: "gain", identifier: 129, description: "Calibrated gain on the weight scale error.", fields: [{ name: "_", shift: 16, type: "u16.16", storage: 4 }], optional: true, packFormat: "u16.16" }, { kind: "const", name: "max_weight", identifier: 261, description: "Maximum supported weight on the scale.", fields: [{ name: "_", unit: "kg", shift: 16, type: "u16.16", storage: 4 }], optional: true, identifierName: "max_reading", packFormat: "u16.16" }, { kind: "const", name: "min_weight", identifier: 260, description: "Minimum recommend weight on the scale.", fields: [{ name: "_", unit: "kg", shift: 16, type: "u16.16", storage: 4 }], optional: true, identifierName: "min_reading", packFormat: "u16.16" }, { kind: "const", name: "weight_resolution", identifier: 264, description: "Smallest, yet distinguishable change in reading.", fields: [{ name: "_", unit: "kg", shift: 16, type: "u16.16", storage: 4 }], optional: true, identifierName: "reading_resolution", packFormat: "u16.16" }, { kind: "const", name: "variant", identifier: 263, description: "The type of physical scale", fields: [{ name: "_", type: "Variant", storage: 1 }], optional: true, identifierName: "variant", packFormat: "u8" }, { kind: "command", name: "calibrate_zero_offset", identifier: 128, description: "Call this command when there is nothing on the scale. If supported, the module should save the calibration data.", fields: [] }, { kind: "command", name: "calibrate_gain", identifier: 129, description: "Call this command with the weight of the thing on the scale.", fields: [{ name: "weight", unit: "g", shift: 10, type: "u22.10", storage: 4 }], packFormat: "u22.10" }], tags: ["8bit"] }, { name: "WIFI", status: "rc", shortId: "wifi", camelName: "wifi", shortName: "wifi", extends: ["_base"], notes: { short: "Discovery and connection to WiFi networks. Separate TCP service can be used for data transfer.", long: "## Connection\n\nThe device controlled by this service is meant to connect automatically, once configured.\nTo that end, it keeps a list of known WiFi networks, with priorities and passwords.\nIt will connect to the available network with numerically highest priority,\nbreaking ties in priority by signal strength (typically all known networks have priority of `0`).\nIf the connection fails (due to wrong password, radio failure, or other problem)\nan `connection_failed` event is emitted, and the device will try to connect to the next eligible network.\nWhen networks are exhausted, the scan is performed again and the connection process restarts.\n\nUpdating networks (setting password, priorties, forgetting) does not trigger an automatic reconnect.\n\n## Captive portals\n\nIf the Wifi is not able to join an AP because it needs to receive a password, it may decide to enter a mode\nwhere it waits for user input. Typical example of this mode would be a captive portal or waiting for a BLE interaction.\nIn that situation, the `status_code` should set to `WaitingForInput`." }, classIdentifier: 413852154, enums: { APFlags: { name: "APFlags", storage: 4, isFlags: true, members: { HasPassword: 1, WPS: 2, HasSecondaryChannelAbove: 4, HasSecondaryChannelBelow: 8, IEEE_802_11B: 256, IEEE_802_11A: 512, IEEE_802_11G: 1024, IEEE_802_11N: 2048, IEEE_802_11AC: 4096, IEEE_802_11AX: 8192, IEEE_802_LongRange: 32768 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "command", name: "last_scan_results", identifier: 128, description: "Return list of WiFi network from the last scan.\nScans are performed periodically while not connected (in particular, on startup and after current connection drops),\nas well as upon `reconnect` and `scan` commands.", fields: [{ name: "results", type: "pipe", storage: 12 }], pipeType: "last_scan_results", packFormat: "b[12]" }, { kind: "pipe_report", name: "results", identifier: 0, description: "Return list of WiFi network from the last scan.\nScans are performed periodically while not connected (in particular, on startup and after current connection drops),\nas well as upon `reconnect` and `scan` commands.", fields: [{ name: "flags", type: "APFlags", storage: 4 }, { name: "reserved", type: "u32", storage: 4, isSimpleType: true }, { name: "rssi", unit: "dB", type: "i8", storage: -1, isSimpleType: true, typicalMin: -100, typicalMax: -20 }, { name: "channel", type: "u8", storage: 1, isSimpleType: true, typicalMin: 1, typicalMax: 13 }, { name: "bssid", type: "u8[6]", storage: 6 }, { name: "ssid", type: "string", storage: 33, maxBytes: 33 }], pipeType: "last_scan_results", packFormat: "u32 u32 i8 u8 b[6] s[33]" }, { kind: "command", name: "add_network", identifier: 129, description: "Automatically connect to named network if available. Also set password if network is not open.", fields: [{ name: "ssid", type: "string0", storage: 0 }, { name: "password", type: "string0", storage: 0, isOptional: true }], packFormat: "z z" }, { kind: "command", name: "reconnect", identifier: 130, description: "Enable the WiFi (if disabled), initiate a scan, wait for results, disconnect from current WiFi network if any,\nand then reconnect (using regular algorithm, see `set_network_priority`).", fields: [] }, { kind: "command", name: "forget_network", identifier: 131, description: "Prevent from automatically connecting to named network in future.\nForgetting a network resets its priority to `0`.", fields: [{ name: "ssid", type: "string", storage: 0 }], packFormat: "s" }, { kind: "command", name: "forget_all_networks", identifier: 132, description: "Clear the list of known networks.", fields: [] }, { kind: "command", name: "set_network_priority", identifier: 133, description: "Set connection priority for a network.\nBy default, all known networks have priority of `0`.", fields: [{ name: "priority", type: "i16", storage: -2, isSimpleType: true }, { name: "ssid", type: "string", storage: 0 }], packFormat: "i16 s" }, { kind: "command", name: "scan", identifier: 134, description: "Initiate search for WiFi networks. Generates `scan_complete` event.", fields: [] }, { kind: "command", name: "list_known_networks", identifier: 135, description: "Return list of known WiFi networks.\n`flags` is currently always 0.", fields: [{ name: "results", type: "pipe", storage: 12 }], pipeType: "list_known_networks", packFormat: "b[12]" }, { kind: "pipe_report", name: "network_results", identifier: 0, description: "Return list of known WiFi networks.\n`flags` is currently always 0.", fields: [{ name: "priority", type: "i16", storage: -2, isSimpleType: true }, { name: "flags", type: "i16", storage: -2, isSimpleType: true }, { name: "ssid", type: "string", storage: 0 }], pipeType: "list_known_networks", packFormat: "i16 i16 s" }, { kind: "ro", name: "rssi", identifier: 257, description: "Current signal strength. Returns -128 when not connected.", fields: [{ name: "_", unit: "dB", type: "i8", storage: -1, isSimpleType: true, typicalMin: -128, typicalMax: -20 }], volatile: true, identifierName: "reading", preferredInterval: 15e3, packFormat: "i8" }, { kind: "rw", name: "enabled", identifier: 1, description: "Determines whether the WiFi radio is enabled. It starts enabled upon reset.", fields: [{ name: "_", type: "bool", storage: 1 }], identifierName: "intensity", packFormat: "u8" }, { kind: "ro", name: "ip_address", identifier: 385, description: "0, 4 or 16 byte buffer with the IPv4 or IPv6 address assigned to device if any.", fields: [{ name: "_", type: "bytes", storage: 16, isSimpleType: true, maxBytes: 16 }], packFormat: "b[16]" }, { kind: "const", name: "eui_48", identifier: 386, description: 'The 6-byte MAC address of the device. If a device does MAC address randomization it will have to "restart".', fields: [{ name: "_", type: "bytes", storage: 6, isSimpleType: true, maxBytes: 6 }], packFormat: "b[6]" }, { kind: "ro", name: "ssid", identifier: 387, description: "SSID of the access-point to which device is currently connected.\nEmpty string if not connected.", fields: [{ name: "_", type: "string", storage: 32, maxBytes: 32 }], packFormat: "s[32]" }, { kind: "event", name: "got_ip", identifier: 1, description: "Emitted upon successful join and IP address assignment.", fields: [], identifierName: "active" }, { kind: "event", name: "lost_ip", identifier: 2, description: "Emitted when disconnected from network.", fields: [], identifierName: "inactive" }, { kind: "event", name: "scan_complete", identifier: 128, description: "A WiFi network scan has completed. Results can be read with the `last_scan_results` command.\nThe event indicates how many networks where found, and how many are considered\nas candidates for connection.", fields: [{ name: "num_networks", type: "u16", storage: 2, isSimpleType: true }, { name: "num_known_networks", type: "u16", storage: 2, isSimpleType: true }], packFormat: "u16 u16" }, { kind: "event", name: "networks_changed", identifier: 129, description: "Emitted whenever the list of known networks is updated.", fields: [] }, { kind: "event", name: "connection_failed", identifier: 130, description: "Emitted when when a network was detected in scan, the device tried to connect to it\nand failed.\nThis may be because of wrong password or other random failure.", fields: [{ name: "ssid", type: "string", storage: 0 }], packFormat: "s" }], tags: [], group: "Iot" }, { name: "Wind direction", status: "rc", shortId: "winddirection", camelName: "windDirection", shortName: "windDirection", extends: ["_base", "_sensor"], notes: { short: "A sensor that measures wind direction." }, classIdentifier: 409725227, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "wind_direction", identifier: 257, description: "The direction of the wind.", fields: [{ name: "_", unit: "\xB0", type: "u16", storage: 2, isSimpleType: true, absoluteMin: 0, absoluteMax: 359 }], volatile: true, identifierName: "reading", preferredInterval: 1e3, packFormat: "u16" }, { kind: "ro", name: "wind_direction_error", identifier: 262, description: "Error on the wind direction reading", fields: [{ name: "_", unit: "\xB0", type: "u16", storage: 2, isSimpleType: true }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u16" }], tags: ["8bit"], group: "Environment" }, { name: "Wind speed", status: "rc", shortId: "windspeed", camelName: "windSpeed", shortName: "windSpeed", extends: ["_base", "_sensor"], notes: { short: "A sensor that measures wind speed." }, classIdentifier: 458824639, enums: {}, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "streaming_samples", identifier: 3, description: "Asks device to stream a given number of samples\n(clients will typically write `255` to this register every second or so, while streaming is required).", fields: [{ name: "_", unit: "#", type: "u8", storage: 1, isSimpleType: true }], internal: true, identifierName: "streaming_samples", packFormat: "u8", derived: "_sensor" }, { kind: "rw", name: "streaming_interval", identifier: 4, description: "Period between packets of data when streaming in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true, defaultValue: 100, typicalMin: 1, typicalMax: 6e4 }], identifierName: "streaming_interval", packFormat: "u32", derived: "_sensor" }, { kind: "const", name: "streaming_preferred_interval", identifier: 258, description: "Preferred default streaming interval for sensor in milliseconds.", fields: [{ name: "_", unit: "ms", type: "u32", storage: 4, isSimpleType: true }], internal: true, optional: true, identifierName: "streaming_preferred_interval", packFormat: "u32", derived: "_sensor" }, { kind: "ro", name: "wind_speed", identifier: 257, description: "The velocity of the wind.", fields: [{ name: "_", unit: "m/s", shift: 16, type: "u16.16", storage: 4 }], volatile: true, identifierName: "reading", preferredInterval: 6e4, packFormat: "u16.16" }, { kind: "ro", name: "wind_speed_error", identifier: 262, description: "Error on the reading", fields: [{ name: "_", unit: "m/s", shift: 16, type: "u16.16", storage: 4 }], volatile: true, optional: true, identifierName: "reading_error", packFormat: "u16.16" }, { kind: "const", name: "max_wind_speed", identifier: 261, description: "Maximum speed that can be measured by the sensor.", fields: [{ name: "_", unit: "m/s", shift: 16, type: "u16.16", storage: 4 }], optional: true, identifierName: "max_reading", packFormat: "u16.16" }], tags: ["8bit"], group: "Environment" }, { name: "WSSK", status: "experimental", shortId: "wssk", camelName: "wssk", shortName: "wssk", extends: ["_base"], notes: { short: "Defines a binary protocol for IoT devices to talk to DeviceScript gateway over encrypted websockets.\nThis is not used as a regular Jacdac service." }, classIdentifier: 330775038, enums: { StreamingType: { name: "StreamingType", storage: 2, members: { Jacdac: 1, Dmesg: 2, Exceptions: 256, TemporaryMask: 255, PermamentMask: 65280, DefaultMask: 256 } }, DataType: { name: "DataType", storage: 1, members: { Binary: 1, JSON: 2 } } }, constants: {}, packets: [{ kind: "report", name: "command_not_implemented", identifier: 3, description: "This report may be emitted by a server in response to a command (action or register operation)\nthat it does not understand.\nThe `service_command` and `packet_crc` fields are copied from the command packet that was unhandled.\nNote that it's possible to get an ACK, followed by such an error report.", fields: [{ name: "service_command", type: "u16", storage: 2, isSimpleType: true }, { name: "packet_crc", type: "u16", storage: 2, isSimpleType: true }], identifierName: "command_not_implemented", packFormat: "u16 u16", derived: "_base" }, { kind: "const", name: "instance_name", identifier: 265, description: "A friendly name that describes the role of this service instance in the device.\nIt often corresponds to what's printed on the device:\nfor example, `A` for button A, or `S0` for servo channel 0.\nWords like `left` should be avoided because of localization issues (unless they are printed on the device).", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "instance_name", packFormat: "s", derived: "_base" }, { kind: "ro", name: "status_code", identifier: 259, description: "Reports the current state or error status of the device. ``code`` is a standardized value from \nthe Jacdac status/error codes. ``vendor_code`` is any vendor specific error code describing the device\nstate. This report is typically not queried, when a device has an error, it will typically\nadd this report in frame along with the announce packet. If a service implements this register,\nit should also support the ``status_code_changed`` event defined below.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code", packFormat: "u16 u16", derived: "_base" }, { kind: "rw", name: "client_variant", identifier: 9, description: "An optional register in the format of a URL query string where the client can provide hints how\nthe device twin should be rendered. If the register is not implemented, the client library can simulate the register client side.", fields: [{ name: "_", type: "string", storage: 0 }], optional: true, identifierName: "client_variant", packFormat: "s", derived: "_base" }, { kind: "event", name: "status_code_changed", identifier: 4, description: "Notifies that the status code of the service changed.", fields: [{ name: "code", type: "u16", storage: 2, isSimpleType: true }, { name: "vendor_code", type: "u16", storage: 2, isSimpleType: true }], optional: true, identifierName: "status_code_changed", packFormat: "u16 u16", derived: "_base" }, { kind: "report", name: "error", identifier: 255, description: "Issued when a command fails.", fields: [{ name: "message", type: "string", storage: 0 }], packFormat: "s" }, { kind: "command", name: "set_streaming", identifier: 144, description: "Enable/disable forwarding of all Jacdac frames, exception reporting, and `dmesg` streaming.", fields: [{ name: "status", type: "StreamingType", storage: 2 }], packFormat: "u16" }, { kind: "command", name: "ping_device", identifier: 145, description: "Send from gateway when it wants to see if the device is alive.\nThe device currently only support 0-length payload.", fields: [{ name: "payload", type: "bytes", storage: 0, isSimpleType: true }], hasReport: true, packFormat: "b" }, { kind: "report", name: "ping_device", identifier: 145, description: "Send from gateway when it wants to see if the device is alive.\nThe device currently only support 0-length payload.", fields: [{ name: "payload", type: "bytes", storage: 0, isSimpleType: true }], secondary: true, packFormat: "b" }, { kind: "command", name: "ping_cloud", identifier: 146, description: "Send from device to gateway to test connection.", fields: [{ name: "payload", type: "bytes", storage: 0, isSimpleType: true }], hasReport: true, packFormat: "b" }, { kind: "report", name: "ping_cloud", identifier: 146, description: "Send from device to gateway to test connection.", fields: [{ name: "payload", type: "bytes", storage: 0, isSimpleType: true }], secondary: true, packFormat: "b" }, { kind: "command", name: "get_hash", identifier: 147, description: "Get SHA256 of the currently deployed program.", fields: [], hasReport: true }, { kind: "report", name: "get_hash", identifier: 147, description: "Get SHA256 of the currently deployed program.", fields: [{ name: "sha256", type: "u8[32]", storage: 32 }], secondary: true, packFormat: "b[32]" }, { kind: "command", name: "deploy_start", identifier: 148, description: "Start deployment of a new program.", fields: [{ name: "size", unit: "B", type: "u32", storage: 4, isSimpleType: true }], hasReport: true, packFormat: "u32" }, { kind: "report", name: "deploy_start", identifier: 148, description: "Start deployment of a new program.", fields: [], secondary: true }, { kind: "command", name: "deploy_write", identifier: 149, description: "Payload length must be multiple of 32 bytes.", fields: [{ name: "payload", type: "bytes", storage: 0, isSimpleType: true }], hasReport: true, packFormat: "b" }, { kind: "report", name: "deploy_write", identifier: 149, description: "Payload length must be multiple of 32 bytes.", fields: [], secondary: true }, { kind: "command", name: "deploy_finish", identifier: 150, description: "Finish deployment.", fields: [], hasReport: true }, { kind: "report", name: "deploy_finish", identifier: 150, description: "Finish deployment.", fields: [], secondary: true }, { kind: "command", name: "c2d", identifier: 151, description: "Upload a labelled tuple of values to the cloud.\nThe tuple will be automatically tagged with timestamp and originating device.", fields: [{ name: "datatype", type: "DataType", storage: 1 }, { name: "topic", type: "string0", storage: 0 }, { name: "payload", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "u8 z b" }, { kind: "report", name: "d2c", identifier: 152, description: "Upload a binary message to the cloud.", fields: [{ name: "datatype", type: "DataType", storage: 1 }, { name: "topic", type: "string0", storage: 0 }, { name: "payload", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "u8 z b" }, { kind: "command", name: "jacdac_packet", identifier: 153, description: "Sent both ways.", fields: [{ name: "frame", type: "bytes", storage: 0, isSimpleType: true }], hasReport: true, packFormat: "b" }, { kind: "report", name: "jacdac_packet", identifier: 153, description: "Sent both ways.", fields: [{ name: "frame", type: "bytes", storage: 0, isSimpleType: true }], secondary: true, packFormat: "b" }, { kind: "report", name: "dmesg", identifier: 154, description: "The `logs` field is string in UTF-8 encoding, however it can be split in the middle of UTF-8 code point.\nControlled via `dmesg_en`.", fields: [{ name: "logs", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "b" }, { kind: "report", name: "exception_report", identifier: 155, description: "The format is the same as `dmesg` but this is sent on exceptions only and is controlled separately via `exception_en`.", fields: [{ name: "logs", type: "bytes", storage: 0, isSimpleType: true }], packFormat: "b" }], tags: ["infrastructure", "devicescript"], group: "Iot" }]; // src/jdom/spec.ts var _serviceSpecifications = services_default; var _serviceSpecificationMap = void 0; function loadServiceSpecifications(specifications) { const previous = _serviceSpecifications; const builtins = services_default || []; const specs = builtins.slice(0); const added = []; const errors = []; if (specifications?.length) { const serviceClasses = new Set( specs.map((s) => s.classIdentifier) ); const shortIds = new Set(specs.map((s) => s.shortId)); for (const spec of specifications) { if (serviceClasses.has(spec.classIdentifier)) { const existingSpec = specs.find( (s) => s.classIdentifier === spec.classIdentifier ); if (JSON.stringify(existingSpec) === JSON.stringify(spec)) continue; errors.push({ message: `classIdentifier 0x${spec.classIdentifier.toString( 16 )} already in use`, spec }); continue; } if (shortIds.has(spec.shortId)) { errors.push({ message: "shortId already in use", spec }); continue; } specs.push(spec); added.push(spec); serviceClasses.add(spec.classIdentifier); shortIds.add(spec.shortId); } } const changed = JSON.stringify(previous) !== JSON.stringify(specs); if (changed) { _serviceSpecifications = specs; _serviceSpecificationMap = void 0; } return { added, errors, changed }; } function serviceMap() { const m = {}; _serviceSpecifications.forEach((spec) => m[spec.shortId] = spec); return m; } function serviceSpecifications() { return _serviceSpecifications.slice(0); } function isInstanceOf(classIdentifier, requiredClassIdentifier) { if (isNaN(classIdentifier)) return false; if (classIdentifier === requiredClassIdentifier) return true; const classSpec = serviceSpecificationFromClassIdentifier(classIdentifier); return !!classSpec?.extends?.some((extend) => { const extendSpec = serviceSpecificationFromName(extend); return !!extendSpec && isInstanceOf(extendSpec.classIdentifier, requiredClassIdentifier); }); } function isInfrastructure(spec) { return spec && ([ SRV_CONTROL, SRV_ROLE_MANAGER, SRV_LOGGER, SRV_SETTINGS, SRV_BOOTLOADER, SRV_PROTO_TEST, SRV_INFRASTRUCTURE, SRV_PROXY, SRV_UNIQUE_BRAIN, SRV_DASHBOARD, SRV_BRIDGE, SRV_DEVICE_SCRIPT_CONDITION, SRV_DEVICE_SCRIPT_MANAGER, SRV_DEVS_DBG ].indexOf(spec.classIdentifier) > -1 || spec.shortId[0] === "_"); } function serviceSpecificationFromName(shortId) { if (!shortId) return void 0; return _serviceSpecifications.find((s) => s.shortId === shortId); } function serviceSpecificationFromClassIdentifier(classIdentifier) { if (isNaN(classIdentifier)) return void 0; let srv = _serviceSpecificationMap?.[classIdentifier]; if (srv) return srv; srv = _serviceSpecifications.find( (s) => s.classIdentifier === classIdentifier ); if (srv) { if (!_serviceSpecificationMap) _serviceSpecificationMap = {}; _serviceSpecificationMap[classIdentifier] = srv; } return srv; } function isSensor(spec) { return spec && spec.packets.some((pkt) => isReading(pkt)) && spec.packets.some((pkt) => pkt.identifier == 3 /* StreamingSamples */); } function isActuator(spec) { return spec && spec.packets.some((pkt) => pkt.identifier === 2 /* Value */) && spec.packets.some((pkt) => pkt.identifier === 1 /* Intensity */); } function isRegister2(pkt) { return pkt && (pkt.kind == "const" || pkt.kind == "ro" || pkt.kind == "rw"); } function isReading(pkt) { return pkt && pkt.kind == "ro" && pkt.identifier == 257 /* Reading */; } var ignoredRegister = [ 259 /* StatusCode */, 265 /* InstanceName */, 4 /* StreamingInterval */, 258 /* StreamingPreferredInterval */, 3 /* StreamingSamples */, 262 /* ReadingError */, 264 /* ReadingResolution */, 260 /* MinReading */, 261 /* MaxReading */, 272 /* MinValue */, 273 /* MaxValue */, 7 /* MaxPower */ ]; function isHighLevelRegister(pkt) { return isRegister2(pkt) && !pkt.lowLevel && !pkt.internal && ignoredRegister.indexOf(pkt.identifier) < 0; } var ignoredEvents = [4 /* StatusCodeChanged */]; function isHighLevelEvent(pkt) { return isEvent(pkt) && !pkt.lowLevel && !pkt.internal && ignoredEvents.indexOf(pkt.identifier) < 0; } function isOptionalReadingRegisterCode(code) { const regs = [ 260 /* MinReading */, 261 /* MaxReading */, 262 /* ReadingError */, 264 /* ReadingResolution */, 258 /* StreamingPreferredInterval */, 4 /* StreamingInterval */, 3 /* StreamingSamples */ ]; return regs.indexOf(code) > -1; } function isIntensity(pkt) { return pkt && pkt.kind == "rw" && pkt.identifier == 1 /* Intensity */; } function isValue(pkt) { return pkt && pkt.kind == "rw" && pkt.identifier == 2 /* Value */; } function isValueOrIntensity(pkt) { return pkt && pkt.kind == "rw" && (pkt.identifier == 2 /* Value */ || pkt.identifier == 1 /* Intensity */); } function isConstRegister(pkt) { return pkt?.kind === "const"; } function isReadOnlyRegister(pkt) { return pkt?.kind !== "rw"; } function isEvent(pkt) { return pkt.kind == "event"; } function isCommand(pkt) { return pkt.kind == "command"; } function isPipeReport(pkt) { return pkt.kind == "pipe_report"; } function isReportOf(cmd, report) { return report.secondary && report.kind == "report" && cmd.kind == "command" && cmd.name == report.name; } function isPipeReportOf(cmd, pipeReport) { return pipeReport.kind == "pipe_report" && cmd.kind == "command" && cmd.pipeType && cmd.pipeType === pipeReport.pipeType; } function isIntegerType(tp) { return /^[ui]\d+(\.|$)/.test(tp) || tp == "pipe_port" || tp == "bool"; } function numberFormatFromStorageType(tp) { switch (tp) { case -1: return 1 /* Int8LE */; case 1: return 2 /* UInt8LE */; case -2: return 3 /* Int16LE */; case 2: return 4 /* UInt16LE */; case -4: return 5 /* Int32LE */; case 4: return 11 /* UInt32LE */; case -8: return 19 /* Int64LE */; case 8: return 17 /* UInt64LE */; case 0: return null; default: return null; } } function numberFormatToStorageType(nf) { switch (nf) { case 1 /* Int8LE */: return -1; case 2 /* UInt8LE */: return 1; case 3 /* Int16LE */: return -2; case 4 /* UInt16LE */: return 2; case 5 /* Int32LE */: return -4; case 11 /* UInt32LE */: return 4; case 19 /* Int64LE */: return -8; case 17 /* UInt64LE */: return 8; default: return null; } } function scaleIntToFloat(v, info) { if (!info.shift) return v; if (info.shift < 0) return v * (1 << -info.shift); else return v / (1 << info.shift); } function scaleFloatToInt(v, info) { if (!info.shift) return v; if (info.shift < 0) return Math.round(v / (1 << -info.shift)); else return Math.round(v * (1 << info.shift)); } function storageTypeRange(tp) { if (tp == 0) throw new Error("no range for 0"); if (tp < 0) { const v = Math.pow(2, -tp * 8 - 1); return [-v, v - 1]; } else { const v = Math.pow(2, tp * 8); return [0, v - 1]; } } function clampToStorage(v, tp) { if (tp == null) return v; const [min, max] = storageTypeRange(tp); if (isNaN(v)) return 0; if (v < min) return min; if (v > max) return max; return v; } function memberValueToString(value, info) { if (value === void 0 || value === null) return ""; switch (info.type) { case "bytes": return toHex2(value); case "string": return value; default: return "" + value; } } function tryParseMemberValue(text, info) { if (!text) return {}; if (info.type === "string") return { value: text }; else if (info.type === "pipe") return {}; else if (info.type === "bytes") { try { return { value: fromHex(text) }; } catch (e) { return { error: "invalid hexadecimal format" }; } } else { const n = isIntegerType(info.type) ? parseInt(text) : parseFloat(text); if (isNaN(n)) return { error: "invalid format" }; else return { value: n }; } } function parseDeviceId(id) { if (!id) return void 0; id = id.replace(/\s/g, ""); if (id.length != 16 || !/^[a-f0-9]+$/i.test(id)) return void 0; return fromHex(id); } function parseDualDeviceId(id) { const rid = parseDeviceId(id); toggleBit(rid, 0); return rid; } function dualDeviceId(id) { return toHex2(parseDualDeviceId(id)); } function isDualDeviceId(left, right) { const lid = parseDeviceId(left); const rid = parseDualDeviceId(right); return bufferEq(lid, rid); } // src/jdom/pack.ts var ch_b = 98; var ch_i = 105; var ch_r = 114; var ch_s = 115; var ch_u = 117; var ch_x = 120; var ch_z = 122; var ch_colon = 58; var ch_sq_open = 91; var ch_sq_close = 93; function numberFormatOfType(tp) { switch (tp) { case "u8": return 2 /* UInt8LE */; case "u16": return 4 /* UInt16LE */; case "u32": return 11 /* UInt32LE */; case "i8": return 1 /* Int8LE */; case "i16": return 3 /* Int16LE */; case "i32": return 5 /* Int32LE */; case "f32": return 13 /* Float32LE */; case "f64": return 14 /* Float64LE */; case "i64": return 19 /* Int64LE */; case "u64": return 17 /* UInt64LE */; default: return null; } } function bufferSlice(buf, start, end) { return buf.slice(start, end); } var TokenParser = class { constructor(fmt) { this.fmt = fmt; this.fp = 0; } parse() { this.div = 1; this.isArray = false; const fmt = this.fmt; while (this.fp < fmt.length) { let endp = this.fp; while (endp < fmt.length && fmt.charCodeAt(endp) != 32) endp++; let word = fmt.slice(this.fp, endp); this.fp = endp + 1; if (!word) continue; const dotIdx = word.indexOf("."); let c0 = word.charCodeAt(0); if ((c0 == ch_i || c0 == ch_u) && dotIdx >= 0) { const sz0 = parseInt(word.slice(1, dotIdx)); const sz1 = parseInt(word.slice(dotIdx + 1)); word = word[0] + (sz0 + sz1); this.div = 1 << sz1; } const c1 = word.charCodeAt(1); if (c1 == ch_sq_open) { this.size = parseInt(word.slice(2)); } else { this.size = -1; } if (word.charCodeAt(word.length - 1) == ch_sq_close && word.charCodeAt(word.length - 2) == ch_sq_open) { word = word.slice(0, -2); this.isArray = true; } this.nfmt = numberFormatOfType(word); this.word = word; if (this.nfmt == null) { if (c0 == ch_r) { if (c1 != ch_colon) c0 = 0; } else if (c0 == ch_s || c0 == ch_b || c0 == ch_x) { if (word.length != 1 && this.size == -1) c0 = 0; } else if (c0 == ch_z) { if (word.length != 1) c0 = 0; } else { c0 = 0; } if (c0 == 0) throw new Error(`invalid format: ${word}`); this.c0 = c0; } else { this.size = sizeOfNumberFormat(this.nfmt); this.c0 = -1; } return true; } return false; } }; function jdunpackCore(buf, fmt, repeat) { const repeatRes = repeat ? [] : null; let res = []; let off = 0; let fp0 = 0; const parser = new TokenParser(fmt); if (repeat && buf.length == 0) return []; while (parser.parse()) { if (parser.isArray && !repeat) { res.push( jdunpackCore( bufferSlice(buf, off, buf.length), fmt.slice(fp0), 1 ) ); return res; } fp0 = parser.fp; let sz = parser.size; const c0 = parser.c0; if (c0 == ch_z) { let endoff = off; while (endoff < buf.length && buf[endoff] != 0) endoff++; sz = endoff - off; } else if (sz < 0) { sz = buf.length - off; } if (parser.nfmt !== null) { let v = getNumber(buf, parser.nfmt, off); if (parser.div != 1) v /= parser.div; res.push(v); off += parser.size; } else { const subbuf = bufferSlice(buf, off, off + sz); if (c0 == ch_z || c0 == ch_s) { let zerop = 0; while (zerop < subbuf.length && subbuf[zerop] != 0) zerop++; res.push(bufferToString(bufferSlice(subbuf, 0, zerop))); } else if (c0 == ch_b) { res.push(subbuf); } else if (c0 == ch_x) { } else if (c0 == ch_r) { res.push(jdunpackCore(subbuf, fmt.slice(fp0), 2)); break; } else { throw new Error(`whoops`); } off += subbuf.length; if (c0 == ch_z) off++; } if (repeat && parser.fp >= fmt.length) { parser.fp = 0; if (repeat == 2) { repeatRes.push(res); res = []; } if (off >= buf.length) break; } } if (repeat == 2) { if (res.length) repeatRes.push(res); return repeatRes; } else { return res; } } function jdunpack(buf, fmt) { if (!buf || !fmt) return void 0; if (fmt === "b") return [buf.slice(0)]; let nf = numberFormatOfType(fmt); if (nf !== null) { let sz = sizeOfNumberFormat(nf); if (buf.length === 4 && nf === 17 /* UInt64LE */) { nf = 11 /* UInt32LE */; sz = 4; } if (buf.length < sz) { throw new Error( `size mistmatch, expected ${fmt} (${sz} bytes), got ${buf.length}` ); } return [getNumber(buf, nf, 0)]; } return jdunpackCore(buf, fmt, 0); } function jdpackCore(trg, fmt, data, off) { let idx = 0; const parser = new TokenParser(fmt); while (parser.parse()) { const c0 = parser.c0; if (c0 == ch_x) { off += parser.size; continue; } const dataItem = data[idx++]; if (c0 == ch_r && dataItem) { const fmt0 = fmt.slice(parser.fp); for (const velt of dataItem) { off = jdpackCore(trg, fmt0, velt, off); } break; } let arr; if (parser.isArray) arr = dataItem; else arr = [dataItem]; for (let v of arr) { if (parser.nfmt !== null) { if (typeof v != "number") throw new Error(`expecting number, got ` + typeof v); if (trg) { v *= parser.div; const st = numberFormatToStorageType( parser.nfmt ); if (parser.div == 1 && (st == 4 || st == -4)) v = 0 | Math.round(v); else if (st != null) v = clampToStorage(Math.round(v), st); setNumber(trg, parser.nfmt, off, v); } off += parser.size; } else { let buf; if (typeof v === "string") { if (c0 == ch_z) buf = stringToBuffer(v + "\0"); else if (c0 == ch_s) buf = stringToBuffer(v); else throw new Error(`unexpected string`); } else if (v && typeof v === "object" && v.length != null) { if (c0 == ch_b) buf = v; else throw new Error(`unexpected buffer`); } else { throw new Error(`expecting string or buffer`); } let sz = parser.size; if (sz >= 0) { if (buf.length > sz) buf = bufferSlice(buf, 0, sz); } else { sz = buf.length; } if (trg) trg.set(buf, off); off += sz; } } } if (data.length > idx) throw new Error(`format '${fmt}' too short`); return off; } function jdpack(fmt, data) { if (!fmt || !data) return void 0; if (fmt === "b") return data[0]?.slice(0); const nf = numberFormatOfType(fmt); if (nf !== null) { const buf = new Uint8Array(sizeOfNumberFormat(nf)); const st = numberFormatToStorageType(nf); let v = data[0]; if (st != null) { if (st == 4 || st == -4) v = Math.round(v) | 0; else v = clampToStorage(Math.round(v), st); } setNumber(buf, nf, 0, v); return buf; } const len = jdpackCore(null, fmt, data, 0); const res = new Uint8Array(len); jdpackCore(res, fmt, data, 0); return res; } function jdpackEqual(fmt, left, right) { if (!left !== !right) return false; if (!left) return true; const leftBuffer = jdpack(fmt, left); const rightBuffer = jdpack(fmt, right); return bufferEq(leftBuffer, rightBuffer); } function packedValuesIsEqual(a, b) { if (a instanceof Uint8Array) { return b instanceof Uint8Array && bufferEq(a, b); } else if (Array.isArray(a)) { const e = Array.isArray(b) && a.length === b.length && a.every((v, i) => packedValuesIsEqual(v, b[i])); return e; } else { return Object.is(a, b); } } // src/jdom/pretty.ts var RegisterType = /* @__PURE__ */ ((RegisterType2) => { RegisterType2[RegisterType2["UInt"] = 0] = "UInt"; RegisterType2[RegisterType2["UIntHex"] = 1] = "UIntHex"; RegisterType2[RegisterType2["Int"] = 2] = "Int"; RegisterType2[RegisterType2["IntArray"] = 3] = "IntArray"; RegisterType2[RegisterType2["String"] = 4] = "String"; return RegisterType2; })(RegisterType || {}); function prettyUnit2(u) { switch (u) { case "us": return "\u03BCs"; case "C": case "Cel": return "\xB0C"; case "K": return "\xB0K"; case "/": case "#": return ""; default: return u; } } function prettyMemberUnit(specification, showDataType) { const parts = [ prettyUnit2(specification.unit), isSet(specification.typicalMin) && `[${specification.typicalMin}, ${specification.typicalMax}]`, isSet(specification.absoluteMin) && `absolute [${specification.absoluteMin}, ${specification.absoluteMax}]` ].filter((f) => isSet(f) && f); if (showDataType) parts.unshift(specification.type); const helperText = parts.join(", "); return helperText; } function prettySize(b) { b = b | 0; if (b === 0) return "0kb"; else if (b < 100) return b + "b"; else if (b < 1e3) return roundWithPrecision(b / 1e3, 2) + "kb"; else if (b < 1e6) return roundWithPrecision(b / 1e3, 1) + "kb"; else return roundWithPrecision(b / 1e6, 1) + "mb"; } function prettyDuration(ms, millis) { let s = ms / 1e3; const precision = millis ? 3 : 0; if (s < 60) return `${roundWithPrecision(s, precision)}s`; let r = ""; const d = Math.floor(s / (24 * 3600)); if (d > 0) { r += d + ":"; s -= d * (24 * 3600); } const h = Math.floor(s / 3600); if (h > 0) { r += h + ":"; s -= h * 3600; } const m = Math.floor(s / 60); if (d > 0 || h > 0 || m > 0) { r += m + ":"; s -= m * 60; } r += roundWithPrecision(s, precision); return r; } function prettyMicroDuration(us) { if (us < 1e3) return `${us}${prettyUnit2("us")}`; else return prettyDuration(us / 1e3); } function shortDeviceId(devid) { const h = hash(fromHex(devid), 30); return String.fromCharCode(65 + h % 26) + String.fromCharCode(65 + idiv(h, 26) % 26) + String.fromCharCode(48 + idiv(h, 26 * 26) % 10) + String.fromCharCode(48 + idiv(h, 26 * 26 * 10) % 10); } function isDeviceId(devid) { return devid && /^[a-f0-9]{16,16}$/i.test(devid); } function toIP(buffer) { if (!buffer) return void 0; if (buffer.length === 4) return `${buffer[0]}.${buffer[1]}.${buffer[2]}.${buffer[3]}`; else return toHex2(buffer, "."); } function toMAC(buffer) { const hex = toHex2(buffer, ":"); return hex; } function toBits(buffer) { let bitString = ""; buffer.forEach((byte) => { for (let i = 7; i >= 0; i--) { bitString += byte >> i & 1; } }); return bitString; } function prettyEnum(enumInfo, numValue, separator = " | ") { if (!enumInfo) return void 0; let humanValue = ""; if (enumInfo.isFlags) { humanValue = ""; let curr = numValue; for (const key of Object.keys(enumInfo.members)) { const val = enumInfo.members[key]; if ((curr & val) == val) { if (humanValue) humanValue += separator; humanValue += key; curr &= ~val; } } if (curr) { if (humanValue) humanValue += separator; humanValue += hexNum(curr); } } else { humanValue = reverseLookup(enumInfo.members, numValue); } return humanValue; } function decodeMember(service, pktInfo, member, pkt, offset) { if (!member) return null; if (pkt.data.length <= offset) return null; let numValue = void 0; let scaledValue = void 0; let value = void 0; let humanValue = void 0; let size = Math.abs(member.storage); const enumInfo = service?.enums[member.type]; const isInt = isIntegerType(member.type) || !!enumInfo; if (service?.classIdentifier === SRV_WIFI && pktInfo.kind === "ro" && pktInfo.identifier === 385 /* IpAddress */ && offset == 0) { value = pkt.data; humanValue = toIP(pkt.data); } else if (service?.classIdentifier === SRV_GPIO && pktInfo.kind === "ro" && pktInfo.identifier === 257 /* State */ && offset == 0) { value = pkt.data; humanValue = toBits(pkt.data); } else if (service?.classIdentifier === SRV_DEVICE_SCRIPT_MANAGER && pktInfo.kind === "const" && pktInfo.identifier === 387 /* RuntimeVersion */ && offset == 0) { value = pkt.data; humanValue = value?.length >= 2 ? `${value[2]}.${value[1]}.${value[0]}` : "?"; } else if (service?.classIdentifier === SRV_WIFI && pktInfo.kind === "const" && pktInfo.identifier === 386 /* Eui48 */ && offset == 0) { value = pkt.data; humanValue = toMAC(pkt.data); } else if (member.isFloat && (size == 4 || size == 8)) { if (size == 4) numValue = pkt.getNumber(13 /* Float32LE */, offset); else numValue = pkt.getNumber(14 /* Float64LE */, offset); value = scaledValue = numValue; if (Math.abs(value) < 10) humanValue = value.toFixed(5); else if (Math.abs(value) < 1e3) humanValue = value.toFixed(3); else if (Math.abs(value) < 1e5) humanValue = value.toFixed(2); else humanValue = "" + value; if (member.unit) humanValue += prettyUnit2(member.unit); } else if (!isInt) { if (member.type == "string0") { let ptr = offset; while (ptr < pkt.data.length) { if (!pkt.data[ptr++]) break; } size = ptr - offset; } const buf = size ? pkt.data.slice(offset, offset + size) : pkt.data.slice(offset); if (member.type == "string" || member.type == "string0") { try { value = fromUTF8(uint8ArrayToString(buf)); } catch { value = uint8ArrayToString(buf); } humanValue = JSON.stringify(value).replace(/\\u0000/g, "\\0"); } else if (member.type == "pipe") { value = buf; const devid = toHex2(buf.slice(0, 8)); const port = read16(buf, 8); humanValue = "pipe to " + shortDeviceId(devid) + " port:" + port; if (pkt?.device?.bus) { const trg = pkt.device.bus.device(devid, true); if (trg) trg.port(port).pipeType = service?.shortId + "." + pktInfo.pipeType + ".report"; } } else { value = buf; humanValue = hexDump(buf); } size = buf.length; } else { const fmt = numberFormatFromStorageType(member.storage); numValue = pkt.getNumber(fmt, offset); value = scaledValue = scaleIntToFloat(numValue, member); if (pkt.device && member.type == "pipe_port") pkt.device.port(value).pipeType = service?.shortId + "." + pktInfo.pipeType + ".command"; if (enumInfo) { if (enumInfo.isFlags) { humanValue = ""; let curr = numValue; for (const key of Object.keys(enumInfo.members)) { const val = enumInfo.members[key]; if ((curr & val) == val) { if (humanValue) humanValue += " | "; humanValue += key; curr &= ~val; } } if (curr) { if (humanValue) humanValue += " | "; humanValue += hexNum(curr); } } else { humanValue = reverseLookup(enumInfo.members, numValue); } } else if (member.type == "bool") { value = !!numValue; humanValue = value ? "true" : "false"; } else if (member.unit === "ms") humanValue = prettyDuration(value); else if (member.unit === "us") humanValue = prettyMicroDuration(value); else if (member.unit || scaledValue != numValue) { let v = scaledValue; if (member.unit) v = roundWithPrecision(v, 3); humanValue = "" + v; if (member.unit) humanValue += prettyUnit2(member.unit); } else { humanValue = scaledValue + ""; if ((scaledValue | 0) == scaledValue && (!member.unit || scaledValue >= 15)) { if (!member.unit) humanValue = hexNum(scaledValue); else humanValue += " (" + hexNum(scaledValue) + ")"; } else if (scaledValue && member.storage == 8) { const did = toHex2(pkt.data.slice(offset, offset + 8)); humanValue += ` (${did} / ${shortDeviceId(did)})`; } } } return { value, numValue, scaledValue, humanValue, description: member.name + ":" + (!humanValue ? "?" : humanValue.indexOf("\n") >= 0 ? "\n" + humanValue.replace(/^/gm, " ") : " " + humanValue), info: member, size }; } function valueToFlags(enumInfo, value) { const r = []; const curr = value; for (const key of Object.keys(enumInfo.members)) { const val = enumInfo.members[key]; if (curr & val) { r.push(val); } } return r; } function flagsToValue(values2) { return values2.reduce((prev, cur) => prev | cur, 0); } function decodeMembers(service, pktInfo, pkt, off = 0) { const fields = pktInfo.fields.slice(0); let startRep = fields.findIndex((f) => f.startRepeats); let fidx = 0; const res = []; while (off < pkt.data.length) { if (fidx >= fields.length && startRep >= 0) fidx = startRep; const member = fields[fidx++]; if (!member) { break; } const decoded = decodeMember(service, pktInfo, member, pkt, off); if (decoded) { off += decoded.size; res.push(decoded); } else { break; } } return res; } function wrapDecodedMembers(decoded) { if (decoded.length == 0) return " {}"; else if (decoded.length == 1 && decoded[0].description.length < 60) return " { " + decoded[0].description + " }"; else return " {\n" + decoded.map((d) => " " + d.description).join("\n") + "\n}"; } function syntheticPktInfo(kind, addr) { return { kind, identifier: addr, name: hexNum(addr), description: "", fields: [ { name: "_", type: "bytes", storage: 0 } ] }; } function decodeRegister(service, pkt) { const isSet2 = pkt.isRegisterSet; const isGet = pkt.isRegisterGet; if (!isSet2 && !isGet) return null; let error = ""; const addr = pkt.serviceCommand & CMD_REG_MASK; let regInfo = service?.packets.find( (p) => isRegister2(p) && p.identifier == addr ); if (!regInfo) { regInfo = syntheticPktInfo("rw", addr); error = `unable to decode register`; } const decoded = decodeMembers(service, regInfo, pkt); if (regInfo.packFormat && pkt.data.length) { try { const recoded = toHex2( jdpack( regInfo.packFormat, jdunpack(pkt.data, regInfo.packFormat) ) ); if (recoded !== void 0 && recoded !== toHex2(pkt.data)) { error = `invalid data packing, ${toHex2( pkt.data )} recoded to ${recoded}`; } } catch (e) { error = `invalid data packing, ${e.message}`; } } let description = ""; if (decoded.length == 0) description = regInfo.name; else if (decoded.length == 1) description = regInfo.name + ": " + decoded[0].humanValue; else description = wrapDecodedMembers(decoded); if (isGet) description = "GET " + description; else description = "SET " + description; return { service, info: regInfo, decoded, description, error }; } function decodeEvent(service, pkt) { if (pkt.isCommand || !pkt.isEvent) return null; const evCode = pkt.eventCode; const evInfo = service?.packets.find( (p) => p.kind == "event" && p.identifier == evCode ) || syntheticPktInfo("event", evCode); const decoded = decodeMembers(service, evInfo, pkt); const description = `EVENT[${pkt.eventCounter}] ${evInfo.name}` + wrapDecodedMembers(decoded); return { service, info: evInfo, decoded, description }; } function decodeCommand(service, pkt) { const kind = pkt.isCommand ? "command" : "report"; const cmdInfo = service?.packets.find( (p) => p.kind == kind && p.identifier == pkt.serviceCommand ) || syntheticPktInfo(kind, pkt.serviceCommand); const decoded = decodeMembers(service, cmdInfo, pkt); const description = (pkt.isCommand ? "CMD " : "REPORT ") + cmdInfo.name + wrapDecodedMembers(decoded); return { service, info: cmdInfo, decoded, description }; } function decodeCRCack(service, pkt) { if (!pkt.isReport || !pkt.isCRCAck) return null; return { service, info: syntheticPktInfo("report", pkt.serviceCommand), decoded: [], description: "CRC-ACK " + hexNum(pkt.serviceCommand) }; } function decodePacket(service, pkt) { const decoded = decodeCRCack(service, pkt) || decodeRegister(service, pkt) || decodeEvent(service, pkt) || decodeCommand(service, pkt); return decoded; } function decodePipe(pkt) { const cmd = pkt.serviceCommand; const pinfo = pkt.device.port(cmd >> PIPE_PORT_SHIFT); if (!pinfo.pipeType) return null; const [servId, pipeType, dir] = pinfo.pipeType.split(/\./); const service = serviceSpecificationFromName(servId); if (!service) return null; const meta = !!(cmd & PIPE_METADATA_MASK); const candidates = service.packets.filter( (p) => p.pipeType == pipeType && /pipe/.test(p.kind) && /meta/.test(p.kind) == meta && /command/.test(p.kind) == (dir == "command") ).filter( (p) => !meta || pkt.getNumber(4 /* UInt16LE */, 0) == p.identifier ); const cmdInfo = candidates[0]; if (cmdInfo) { const decoded = decodeMembers(service, cmdInfo, pkt, meta ? 4 : 0); const description = cmdInfo.kind.toUpperCase() + " " + cmdInfo.name + wrapDecodedMembers(decoded); return { service, info: cmdInfo, decoded, description }; } return null; } function decodePacketData(pkt) { try { if (pkt.device && pkt.isPipe) { const info = decodePipe(pkt); if (info) return info; } const serviceClass2 = pkt.serviceClass; const service = serviceSpecificationFromClassIdentifier(serviceClass2); return decodePacket(service, pkt); } catch (error) { console.error(error, { error, pkt, data: toHex2(pkt.data) }); throw error; } } function reverseLookup(map, n) { for (const k of Object.keys(map)) { if (map[k] == n) return k; } return hexNum(n); } function serviceClass(name) { const serv = serviceSpecificationFromName(name); return serv ? serv.classIdentifier : -1; } function serviceName(serviceClass2) { if (!isSet(serviceClass2)) return "?"; const serv = serviceSpecificationFromClassIdentifier(serviceClass2); return serv ? serv.name.toUpperCase() : "?"; } function serviceShortIdOrClass(serviceClass2, hexPrefix = "0x") { if (!isSet(serviceClass2)) return "?"; const serv = serviceSpecificationFromClassIdentifier(serviceClass2); return serv?.shortId || `${hexPrefix}${serviceClass2.toString(16)}`; } function deviceServiceName(pkt) { const srv_class = pkt?.device?.serviceClassAt(pkt.serviceIndex); const serv_id = serviceName(srv_class); return `${pkt?.device?.shortId || "?"}/${serv_id}:${pkt.serviceIndex}`; } function commandName(n, serviceClass2) { let pref = ""; if ((n & CMD_TOP_MASK) == CMD_SET_REG) pref = "SET["; else if ((n & CMD_TOP_MASK) == CMD_GET_REG) pref = "GET["; if (pref) { const reg = n & CMD_REG_MASK; let regName = SystemReg[reg]?.toLowerCase(); if (regName === void 0) { const serviceSpec = serviceSpecificationFromClassIdentifier(serviceClass2); regName = serviceSpec?.packets.find( (pkt) => isRegister2(pkt) && pkt.identifier === reg )?.name; } return pref + (regName !== void 0 ? regName : `x${reg.toString(16)}`) + "]"; } let r = SystemCmd[n]?.toLowerCase(); if (r === void 0) { const serviceSpec = serviceSpecificationFromClassIdentifier(serviceClass2); r = serviceSpec?.packets.find( (pkt) => (pkt.kind === "command" || pkt.kind === "report") && pkt.identifier === n )?.name; } return r; } function num2str(n) { return n + " (0x" + n.toString(16) + ")"; } function toAscii(d) { let r = ""; for (let i = 0; i < d.length; ++i) { const c = d[i]; if (c < 32 || c >= 127) r += "."; else r += String.fromCharCode(c); } return r; } function hexDump(d) { const chunk = 32; if (d.length <= chunk) return toHex2(d) + "\xA0|\xA0" + toAscii(d); const a = toArray(d); let r = ""; for (let i = 0; i < d.length; i += chunk) { if (i + chunk >= d.length) { let s = toHex2(a.slice(i)); while (s.length < chunk * 2) s += " "; r += s + "\xA0|\xA0" + toAscii(a.slice(i)); } else { r += hexDump(a.slice(i, i + chunk)) + "\n"; } } return r; } function printPacket(pkt, opts = {}) { const frame_flags = pkt.frameFlags; const devname = pkt.friendlyDeviceName; const service_name = pkt.friendlyServiceName; const cmdname = pkt.friendlyCommandName || hexNum(pkt.serviceCommand); const sender = pkt.sender; if (opts.skipResetIn && pkt.serviceIndex === JD_SERVICE_INDEX_CTRL && pkt.serviceCommand === (CMD_SET_REG | 128 /* ResetIn */)) return ""; let pdesc = `${devname}/${service_name}: ${cmdname}; sz=${pkt.size}`; if (frame_flags & JD_FRAME_FLAG_COMMAND) pdesc = "to " + pdesc; else pdesc = "from " + pdesc; if (frame_flags & JD_FRAME_FLAG_ACK_REQUESTED) pdesc = `[ack:${hexNum(pkt.crc)}] ` + pdesc; const d = pkt.data; if (pkt.device && pkt.serviceIndex == JD_SERVICE_INDEX_CTRL && pkt.serviceCommand == CMD_ADVERTISEMENT_DATA) { if (pkt.device.lastServiceUpdate < pkt.timestamp) { if (opts.skipRepeatedAnnounce) return ""; else pdesc = " ====== " + pdesc; } else { pdesc += "; Announce services: " + pkt.device.serviceClasses.map(serviceName).join(", "); } } else if (pkt.isRepeatedEvent) { pdesc = ` ------ ${pdesc} EVENT[${pkt.eventCounter}]`; } else { const decoded = pkt.decoded; if (decoded) { pdesc += "; " + decoded.description; } else if (0 < d.length && d.length <= 4) { const v0 = pkt.uintData, v1 = pkt.intData; pdesc += "; " + num2str(v0); if (v0 != v1) pdesc += "; signed: " + num2str(v1); } else if (d.length) { pdesc += "; " + hexDump(d); } } if (sender) pdesc += ` (${sender})`; return (!isNaN(pkt.timestamp) && opts?.showTime ? Math.round(pkt.timestamp) + "ms: " : "") + pdesc; } // src/jdom/packet.ts var { warn, debug } = console; function isLargeFrame(frame) { return frame[2] === 255; } var _Packet = class { constructor() { this._meta = void 0; this.key = _Packet._nextKey++; } static fromBinary(data, timestamp2) { if (!data || data.length > 252) return void 0; const p = new _Packet(); p._header = data.slice(0, JD_SERIAL_HEADER_SIZE); p._data = data.slice( JD_SERIAL_HEADER_SIZE, JD_SERIAL_HEADER_SIZE + p.size ); p.sender = data._jacdac_sender; p.timestamp = timestamp2 ?? data._jacdac_timestamp; return p; } static from(service_command, data) { const p = new _Packet(); p._header = new Uint8Array(JD_SERIAL_HEADER_SIZE); p.data = data; p.serviceCommand = service_command; return p; } static onlyHeader(service_command) { return _Packet.from(service_command, new Uint8Array(0)); } toBuffer() { const res = bufferConcat(this._header, this._data); res[2] = this._data.length + 4; write16(res, 0, crc(res.slice(2))); res._jacdac_sender = this.sender; res._jacdac_timestamp = this.timestamp; return res; } get header() { return this._header.slice(0); } get deviceIdentifier() { return toHex2(this._header.slice(4, 4 + 8)); } set deviceIdentifier(id) { if (id !== this.deviceIdentifier) { const idb = fromHex(id); if (idb.length != 8) throwError("Invalid id"); if (this.isMultiCommand) throwError("Invalid multicast"); this._header.set(idb, 4); this._decoded = void 0; this.device = void 0; } } get frameFlags() { return this._header[3]; } set frameFlags(v) { this._header[3] = v; } get isMultiCommand() { return !!(this.frameFlags & JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS); } get size() { return this._header[12]; } get requiresAck() { return this.frameFlags & JD_FRAME_FLAG_ACK_REQUESTED ? true : false; } set requiresAck(ack) { if (ack != this.requiresAck) this._header[3] ^= JD_FRAME_FLAG_ACK_REQUESTED; this._decoded = void 0; } get serviceIndex() { return this._header[13] & JD_SERVICE_INDEX_MASK; } set serviceIndex(value) { if (value == null) throw new Error("service_index not set"); this._header[13] = this._header[13] & JD_SERVICE_INDEX_INV_MASK | value; this._decoded = void 0; } get serviceClass() { if (this.isMultiCommand) return read32(this._header, 4); if (this.serviceIndex === 0) return SRV_CONTROL; return this.device?.serviceClassAt(this.serviceIndex); } get crc() { return read16(this._header, 0); } get serviceCommand() { return read16(this._header, 14); } set serviceCommand(cmd) { write16(this._header, 14, cmd); this._decoded = void 0; } get isRegisterSet() { return this.serviceIndex <= JD_SERVICE_INDEX_MAX_NORMAL && this.serviceCommand >> 12 == CMD_SET_REG >> 12; } get isRegisterGet() { return this.serviceIndex <= JD_SERVICE_INDEX_MAX_NORMAL && this.serviceCommand >> 12 == CMD_GET_REG >> 12; } // TODO rename to registerCode get registerIdentifier() { if (!this.isRegisterGet && !this.isRegisterSet) return void 0; return this.serviceCommand & CMD_REG_MASK; } get isEvent() { return this.serviceIndex <= JD_SERVICE_INDEX_MAX_NORMAL && (this.serviceCommand & CMD_EVENT_MASK) !== 0; } get eventCode() { return this.isEvent ? this.serviceCommand & CMD_EVENT_CODE_MASK : void 0; } get eventCounter() { return this.isEvent ? this.serviceCommand >> CMD_EVENT_COUNTER_POS & CMD_EVENT_COUNTER_MASK : void 0; } get isCRCAck() { return this.serviceIndex === JD_SERVICE_INDEX_CRC_ACK; } get isPipe() { return this.serviceIndex === JD_SERVICE_INDEX_PIPE; } get pipePort() { return this.isPipe && this.serviceCommand >> PIPE_PORT_SHIFT; } get pipeCount() { return this.isPipe && this.serviceCommand & PIPE_COUNTER_MASK; } get data() { return this._data; } set data(buf) { if (buf.length > JD_SERIAL_MAX_PAYLOAD_SIZE) throw Error( `jacdac packet length too large, ${buf.length} > ${JD_SERIAL_MAX_PAYLOAD_SIZE} bytes` ); this._header[12] = buf.length; this._data = buf; this._decoded = void 0; } jdunpack(fmt) { return this._data && fmt && jdunpack(this._data, fmt) || []; } get uintData() { let buf = this._data; if (buf.length == 0) return void 0; if (buf.length < 4) buf = bufferConcat(buf, new Uint8Array(4)); if (buf.length == 8) return read32(buf, 0) + read32(buf, 4) * 4294967296; return read32(buf, 0); } get stringData() { return this._data && bufferToString(this._data); } get intData() { let fmt; switch (this._data.length) { case 0: return void 0; case 1: fmt = 1 /* Int8LE */; break; case 2: case 3: fmt = 3 /* Int16LE */; break; default: fmt = 5 /* Int32LE */; break; } return this.getNumber(fmt, 0); } get isAnnounce() { return this.serviceIndex == JD_SERVICE_INDEX_CTRL && this.isReport && this.serviceCommand == 0 /* Announce */; } get isRepeatedAnnounce() { return this.isAnnounce && this.device?.lastServiceUpdate < this.timestamp; } get decoded() { if (!this._decoded) this._decoded = decodePacketData(this); return this._decoded; } get meta() { if (!this._meta) this._meta = {}; return this._meta; } clone() { const pkt = new _Packet(); pkt._header = this._header.slice(); pkt._data = this._data.slice(); pkt.timestamp = this.timestamp; return pkt; } cloneForDevice(deviceId, serviceIndex) { const idb = fromHex(deviceId); if (idb.length != 8) throwError("Invalid id"); if (!this.isMultiCommand) throwError("Must be multi command"); const pkt = _Packet.fromBinary(this.toBuffer(), this.timestamp); pkt.frameFlags &= ~JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS; pkt._header.set(idb, 4); pkt._decoded = void 0; pkt.sender = void 0; pkt.serviceIndex = serviceIndex; return pkt; } compress(stripped) { if (stripped.length == 0) return; let sz = -4; for (const s of stripped) { sz += s.length; } const data = new Uint8Array(sz); this._header.set(stripped[0], 12); data.set(stripped[0].slice(4), 0); sz = stripped[0].length - 4; for (const s of stripped.slice(1)) { data.set(s, sz); sz += s.length; } this._data = data; this._decoded = void 0; } withFrameStripped() { return bufferConcat(this._header.slice(12, 12 + 4), this._data); } getNumber(fmt, offset) { return getNumber(this._data, fmt, offset); } get isCommand() { return !!(this.frameFlags & JD_FRAME_FLAG_COMMAND); } set isCommand(value) { if (value) this._header[3] |= JD_FRAME_FLAG_COMMAND; else this._header[3] &= ~JD_FRAME_FLAG_COMMAND; this._decoded = void 0; } get isReport() { return !this.isCommand; } assignDevice(bus) { if (!this.isMultiCommand && !this.device) this.device = bus.device(this.deviceIdentifier, false, this); } toString() { let msg = `${shortDeviceId(this.deviceIdentifier)}/${this.serviceIndex}[${this.frameFlags}]: 0x${this.serviceCommand.toString(16)} sz=${this.size}`; if (this.size < 20) msg += ": " + toHex2(this.data); else msg += ": " + toHex2(this.data.slice(0, 20)) + "..."; return msg; } sendCoreAsync(bus) { const buf = this.toBuffer(); this._header[0] = buf[0]; this._header[1] = buf[1]; this._header[2] = buf[2]; this.assignDevice(bus); return bus.sendPacketAsync(this); } sendReportAsync(dev) { if (!dev) return Promise.resolve(); this.deviceIdentifier = dev.deviceId; this.device = dev; return this.sendCoreAsync(dev.bus); } sendCmdAsync(dev) { if (!dev) return Promise.resolve(); this.deviceIdentifier = dev.deviceId; this.device = dev; this.isCommand = true; return this.sendCoreAsync(dev.bus); } sendAsMultiCommandAsync(bus, service_class) { this._header[3] |= JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS | JD_FRAME_FLAG_COMMAND; write32(this._header, 4, service_class); write32(this._header, 8, JD_DEVICE_IDENTIFIER_BROADCAST_HIGH_MARK); this.serviceIndex = JD_SERVICE_INDEX_BROADCAST; return this.sendCoreAsync(bus); } static fromFrame(frame, timestamp2, skipCrc = false) { return frameToPackets(frame, timestamp2, skipCrc); } static jdpacked(service_command, fmt, nums) { return _Packet.from(service_command, jdpack(fmt, nums)); } // helpers get friendlyDeviceName() { if (this.isMultiCommand) return "*"; return this.device?.friendlyName || shortDeviceId(this.deviceIdentifier); } get friendlyServiceName() { let service_name; if (this.isCRCAck) { service_name = "CRC-ACK"; } else if (this.isPipe) { service_name = "PIPE"; } else { const sc = this.serviceClass; if (sc === void 0) return `(${this.serviceIndex})`; else { const serv_id = serviceName(sc); service_name = `${serv_id === "?" ? hexNum(sc) : serv_id} (${this.serviceIndex})`; } } return service_name; } get friendlyCommandName() { const cmd = this.serviceCommand; let cmdname; if (this.isCRCAck) { cmdname = hexNum(cmd); } else if (this.isPipe) { cmdname = `port:${cmd >> PIPE_PORT_SHIFT} cnt:${cmd & PIPE_COUNTER_MASK}`; if (cmd & PIPE_METADATA_MASK) cmdname += " meta"; if (cmd & PIPE_CLOSE_MASK) cmdname += " close"; } else if (this.isEvent) { const spec = serviceSpecificationFromClassIdentifier( this.serviceClass ); const code = this.eventCode; const pkt = spec?.packets.find( (pkt2) => pkt2.kind === "event" && pkt2.identifier === code ); cmdname = pkt?.name; } else { cmdname = commandName(cmd, this.serviceClass); } return cmdname; } }; var Packet = _Packet; Packet._nextKey = 1; function frameToPackets(frame, timestamp2, skipCrc = false) { if (timestamp2 === void 0) timestamp2 = frame._jacdac_timestamp; const size = frame.length < 12 ? 0 : frame[2]; if (frame.length < size + 12) { warn( `${timestamp2 | 0}ms: got only ${frame.length} bytes; expecting ${size + 12}` ); return []; } else if (size < 4) { warn(`${timestamp2 | 0}ms: empty packet`); return []; } else { if (frame.length != 12 + size) { warn(`${timestamp2 | 0}ms: unexpected packet len: ${frame.length}`); return []; } if (!skipCrc) { const computed = crc(frame.slice(2, size + 12)); const actual = read16(frame, 0); if (actual != computed) { warn( `${timestamp2 | 0}ms: crc mismatch; sz=${size} got:${actual}, exp:${computed}, ${toHex2( frame )}` ); return []; } } const res = []; for (let ptr = 12; ptr < 12 + size; ) { const psz = frame[ptr] + 4; const sz = ALIGN(psz); if (ptr + psz > 12 + size) { warn( `${timestamp2 | 0}ms: invalid frame compression, res len=${res.length}` ); break; } const pkt = bufferConcat( frame.slice(0, 12), frame.slice(ptr, ptr + psz) ); const p = Packet.fromBinary(pkt); p.timestamp = timestamp2; p.sender = frame._jacdac_sender; res.push(p); if (res.length > 1) p.requiresAck = false; ptr += sz; } return res; } } // src/jdom/trace/trace.ts var TRACE_OVERSHOOT = 1.1; function stack() { return new Error().stack; } function cleanStack(text) { return text?.split(/\n/g).slice(2).join("\n").replace(/webpack-internal:\/\/\//g, "").replace(/https:\/\/microsoft\.github\.io\/jacdac-docs/g, ""); } function serializeToTrace(frame, start, bus, options) { const data = toHex2(frame).padEnd(60, " "); const t0 = (frame._jacdac_timestamp || 0) - (start || 0); let t = t0.toFixed(3).padEnd(10, " "); let descr = frame._jacdac_meta?.[META_TRACE_DESCRIPTION] ?? ""; if (options) { const perpkt = Packet.fromFrame(frame, void 0, true).map((pkt) => { pkt.sender = void 0; if (bus) pkt.assignDevice(bus); return printPacket(pkt, options); }); if (perpkt.length == 1 && perpkt[0].length < 70 && !perpkt[0].includes("\n")) descr += `${perpkt[0]} (${frame._jacdac_sender ?? "loop"})`; else { descr += ` (${frame._jacdac_sender ?? "loop"}) `; descr += perpkt.join("\n") + "\n"; } } else if (!descr) { descr = Packet.fromFrame(frame, void 0, true).map((pkt) => { if (bus) pkt.assignDevice(bus); return printPacket(pkt, {}).replace(/\r?\n/g, " "); }).join(" ;; "); } let msg = `${t} ${data} ${descr}`; const trace = frame._jacdac_meta?.[META_TRACE]; if (trace) msg += "\n" + cleanStack(trace); return msg; } var Trace = class { /** * Constructs a new empty trace or from an existing list of packets * @param frames list of frames/packets * @param description description of the trace */ constructor(frames = [], options) { this.frames = frames; this.id = randomDeviceId(); this.description = options?.description; this.maxLength = options?.maxLength; } /** * Number of packets in trace */ get length() { return this.frames.length; } /** * Duration in milliseconds between the first and last packet. */ get duration() { if (!this.frames.length) return 0; return this.frames[this.frames.length - 1]._jacdac_timestamp - this.frames[0]._jacdac_timestamp; } /** * _jacdac_timestamp of the first packet, defaults to 0 if trace is empty. */ get startTimestamp() { return this.frames[0]?._jacdac_timestamp || 0; } /** * _jacdac_timestamp of the last packet, defaults to 0 if trace is empty. */ get endTimestamp() { return this.frames[this.frames.length - 1]?._jacdac_timestamp || 0; } /** * Appends a frame to the trace * @param frame frame/packet to add */ addFrame(frame) { if (!frame._jacdac_meta) frame._jacdac_meta = {}; if (frame._jacdac_meta[this.id]) { return; } frame._jacdac_meta[this.id] = true; const copy = frame.slice(); copy._jacdac_sender = frame._jacdac_sender; copy._jacdac_timestamp = frame._jacdac_timestamp; this.frames.push(copy); if (this.maxLength > 0 && this.frames.length > this.maxLength * TRACE_OVERSHOOT) { this.frames = this.frames.slice(-this.maxLength); } } /** * Gets a text-rendered view of the trace * @param length maximum number of elements * @returns text where each line is a packet */ serializeToText(length) { const start = this.startTimestamp; let pkts = this.frames; if (length > 0) pkts = pkts.slice(-length); const text = pkts.map( (pkt) => serializeToTrace(pkt, start, this.resolutionBus) ); if (this.description) { text.unshift(this.description); text.unshift(""); } return text.join("\n"); } resolveDevices(bus) { this.resolutionBus = bus; } toPackets(bus) { const res = []; for (const frame of this.frames) for (const pkt of Packet.fromFrame(frame, void 0, true)) { if (bus) pkt.assignDevice(bus); res.push(pkt); } return res; } }; // src/jdom/eventsource.ts function normalizeEventNames(eventNames) { if (!eventNames) eventNames = []; if (typeof eventNames === "string") eventNames = [eventNames]; return eventNames; } function dependencyId(nodes) { if (Array.isArray(nodes)) return nodes?.map((node) => node?.nodeId || "?").join(",") || ""; else return nodes?.nodeId || ""; } var nextNodeId = 0; var JDCancellationToken = class { constructor() { this.subscriptions = []; } mount(...items) { this.subscriptions.push(...items); } unmount() { const us = this.subscriptions; this.subscriptions = []; us.forEach((unsub) => unsub()); } }; var JDEventSource = class { /** * @internal */ constructor() { /** * Gets an internal unique node identifier, mostly used for debugging. * @category JDOM */ this.nodeId = nextNodeId++; this.listeners = {}; /** * Gets a counter of event emit calls. * @category JDOM */ this.eventStats = {}; /** * Gets a counter map from events to new listener counts * @category JDOM */ this.newListenerStats = void 0; } /** * Registers a handler for one or more events * @param eventName name or names of the events to subscribe * @param handler handler to register * @returns current object instance * @category JDOM */ on(eventName, handler) { if (!handler) return this; normalizeEventNames(eventName).forEach( (eventName2) => this.addListenerInternal(eventName2, handler, false) ); return this; } /** * Unregisters a handler for one or more events * @param eventName name or names of the events to subscribe * @param handler handler to unregister * @returns current object instance * @category JDOM */ off(eventName, handler) { normalizeEventNames(eventName).forEach( (eventName2) => this.removeListenerInternal(eventName2, handler) ); return this; } /** * Registers a handler for one or more events to run only once. * @param eventName name or names of the events to subscribe * @param handler handler to execute * @returns current object instance * @category JDOM */ once(eventName, handler) { normalizeEventNames(eventName).forEach( (eventName2) => this.addListenerInternal(eventName2, handler, true) ); return this; } /** * Awaits an event with a timeout. Throws JacdacError with timeout if operation does not return. * @param eventName * @param timeout */ async awaitOnce(eventName, token) { if (!eventName) return; const p = new Promise((resolve) => { const handler = () => resolve?.(); this.once(eventName, handler); token?.mount(() => this.off(eventName, handler)); }); return p; } addListenerInternal(eventName, handler, once) { if (!eventName || !handler) { return; } const eventListeners = this.listeners[eventName] || (this.listeners[eventName] = []); const listener = eventListeners.find( (listener2) => listener2.handler === handler ); if (listener) { listener.once = !!once; return; } eventListeners.push({ handler, once: !!once, // debug only collection of trace for leak detection stackTrace: Flags.diagnostics && stack() }); this.emit(NEW_LISTENER, eventName, handler); if (Flags.diagnostics) { if (!this.newListenerStats) this.newListenerStats = {}; this.newListenerStats[eventName] = (this.newListenerStats[eventName] || 0) + 1; } } removeListenerInternal(eventName, handler) { if (!eventName || !handler) return; const eventListeners = this.listeners[eventName]; if (eventListeners) { for (let i = 0; i < eventListeners.length; ++i) { const listener = eventListeners[i]; if (handler === listener.handler) { eventListeners.splice(i, 1); this.emit(REMOVE_LISTENER, eventName, handler); return; } } } } /** * Synchronously calls each of the listeners registered for the event named eventName, * in the order they were registered, passing the supplied arguments to each. * @param eventName * @param args * @category JDOM */ emit(eventName, ...args) { if (!eventName) return false; this.eventStats[eventName] = (this.eventStats[eventName] || 0) + 1; const eventListeners = this.listeners[eventName]; if (!eventListeners || eventListeners.length == 0) { if (eventName == ERROR && Flags.diagnostics) console.debug(args[0]); return false; } for (let i = 0; i < eventListeners.length; ++i) { const listener = eventListeners[i]; const handler = listener.handler; if (listener.once) { eventListeners.splice(i, 1); --i; } try { handler.apply(null, args); } catch (e) { if (eventName !== ERROR) this.emit(ERROR, e); } } return true; } /** * Gets the number of listeners for a given event * @param eventName name of the event * @returns number of registered handlers * @category JDOM */ listenerCount(eventName) { if (!eventName) return 0; const listeners = this.listeners[eventName]; return listeners?.length || 0; } /** * Gets the list stack trace where an event was registered. Only enabled if ``Flags.debug`` is true. * @param eventName name of the event * @returns stack traces where a listener was added * @category JDOM */ listenerStackTraces(eventName) { const listeners = this.listeners[eventName]; return listeners?.map((listener) => listener.stackTrace); } /** * Returns an array listing the events for which the emitter has registered listeners. * @category JDOM */ eventNames() { return Object.keys(this.listeners); } /** * Creates an observable from the given event * @param eventName * @category JDOM */ observe(eventName) { return new EventObservable(this, normalizeEventNames(eventName)); } /** * Subscribes to an event and returns the unsubscription handler * @param eventName * @param next * @category JDOM */ subscribe(eventName, next) { const observer = this.observe(eventName); return observer.subscribe({ next }).unsubscribe; } /** * Gets a counter for the ``CHANGE`` event. * @category JDOM */ get changeId() { return this.eventStats[CHANGE] || 0; } }; var JDSubscriptionScope = class { constructor() { this.unsubscribers = []; this._unmounted = false; } get unmounted() { return this._unmounted; } mount(unsubscribe) { this._unmounted = false; if (unsubscribe && this.unsubscribers.indexOf(unsubscribe) < 0) this.unsubscribers.push(unsubscribe); return unsubscribe; } unmount() { const us = this.unsubscribers; this.unsubscribers = []; us.forEach((u) => u()); this._unmounted = true; } }; var EventObservable = class { constructor(eventEmitter, eventNames) { this.eventEmitter = eventEmitter; this.eventNames = eventNames; } subscribe(observer) { if (observer.next) this.eventEmitter.on(this.eventNames, observer.next); if (observer.error) this.eventEmitter.on(ERROR, observer.error); return { unsubscribe: () => { if (observer.next) this.eventEmitter.off(this.eventNames, observer.next); if (observer.error) this.eventEmitter.off(ERROR, observer.error); } }; } }; // src/jdom/node.ts var JDNode = class extends JDEventSource { constructor() { super(); } /** * A human friendly name * @category JDOM */ get friendlyName() { return this.name; } /** * Gets a databag to store custom information * @category JDOM */ get nodeData() { if (!this._nodeData) this._nodeData = {}; return this._nodeData; } /** * Emit event in current node and parent nodes * @param event event to emit * @param arg event arguments * @category JDOM */ emitPropagated(event, arg) { let current = this; while (current) { current.emit(event, arg || this); current = current.parent; } } /** * @hidden */ toString() { return this.friendlyName; } }; // src/jdom/servicemembernode.ts var JDServiceMemberNode = class extends JDNode { /** * @internal */ constructor(service, code, isPacket) { super(); this._notImplemented = false; this._specification = null; this.service = service; this.code = code; this.isPacket = isPacket; } /** * Gets the node identifier in the JDOM tree * @category JDOM */ get id() { return `${this.nodeKind}:${this.service.device.deviceId}:${this.service.serviceIndex.toString(16)}:${this.code.toString(16)}`; } /** * Gets the event name, if specified. * @category JDOM */ get name() { return this.specification?.name || this.code.toString(16); } /** * Gets the qualitified event name, if specified. * @category JDOM */ get qualifiedName() { return `${this.service.qualifiedName}.${this.name}`; } /** * Gets the event specification if known. * @category Specification */ get specification() { if (this._specification === null) this._specification = this.service.specification?.packets.find( (packet) => this.isPacket(packet) && packet.identifier === this.code ); return this._specification; } /** * Gets the parent service client instance. * @category JDOM */ get parent() { return this.service; } /** * Gets the event friendly name. * @category JDOM */ get friendlyName() { const parts = [this.service.friendlyName, this.name]; return parts.join("."); } /** * Indicates if the member is not implemented on the server side * @category JDOM */ get notImplemented() { return this._notImplemented; } /** * Internal * @internal */ setNotImplemented() { if (!this._notImplemented) { this._notImplemented = true; this.emit(CHANGE); } } }; // src/jdom/flashing.ts var BL_SUBPAGE_SIZE = 208; var BL_RETRIES = 15; var BL_SESSION_DELAY = 5; var BL_PAGE_DELAY = 5; var _startTime = 0; var uf2ExtTags = { version: -10471356, name: -6622621, pageSize: 780791, productIdentifier: 13149993 }; function timestamp() { if (!_startTime) _startTime = Date.now(); return Date.now() - _startTime; } function log(msg) { if (Flags.diagnostics) console.debug(`BL [${timestamp()}ms]: ${msg}`); } var FirmwareUpdater = class extends JDEventSource { constructor(bus, blob) { super(); this.bus = bus; this.blob = blob; } /** * Flash firmware blob onto device * @param bus * @param blob * @param updateCandidates * @param ignoreFirmwareCheck * @param progress * @returns * @category Firmware */ async flash(updateCandidates, ignoreFirmwareCheck) { if (!updateCandidates?.length) return; const { bus, blob } = this; _startTime = Date.now(); log(`resetting ${updateCandidates.length} device(s)`); const bootloaderDeviceIds = []; for (const d of updateCandidates) { const device = bus.device(d.deviceId, true); if (!device) { log("device not found"); return; } if (!device.bootloader) { log(`resetting ${device}`); bootloaderDeviceIds.push(dualDeviceId(device.deviceId)); await device.sendCtrlCommand(130 /* Reset */); } else { bootloaderDeviceIds.push(device.deviceId); } } const allFlashers = await createFlashers(this, bootloaderDeviceIds); const flashers = allFlashers.filter( (f) => !!ignoreFirmwareCheck || f.dev_class == blob.productIdentifier ).filter( (f) => updateCandidates.find( (uc) => uc.deviceId === f.device.deviceId || isDualDeviceId(uc.deviceId, f.device.deviceId) ) ); if (!flashers.length) { log(`no devices to flash`); return; } if (flashers.length != updateCandidates.length) { console.error( `expected ${updateCandidates.length} flashers, got ${flashers.length}` ); return; } log(`flashing ${blob.name}`); flashers[0].classClients = flashers; const unmounts = []; try { flashers.forEach((f) => { f.device.firmwareUpdater = this; const dualDevice = bus.device( dualDeviceId(f.device.deviceId), true ); if (dualDevice) dualDevice.firmwareUpdater = this; unmounts.push(() => { f.device.firmwareUpdater = void 0; if (dualDevice) dualDevice.firmwareUpdater = void 0; }); }); await flashers[0].flashFirmwareBlob(blob); } finally { unmounts.forEach((u) => u()); } } }; var FlashClient = class { constructor(updater, adpkt) { this.updater = updater; const d = bufferToArray(adpkt.data, 11 /* UInt32LE */); this.pageSize = d[1]; this.flashSize = d[2]; this.dev_class = d[3]; this.device = adpkt.device; this.handlePacket = this.handlePacket.bind(this); } get bus() { return this.updater.bus; } get progress() { return this.progressTotal ? this.progressIndex / this.progressTotal : 0; } handlePacket(pkt) { if (pkt.serviceCommand == 128 /* PageData */) this.lastStatus = pkt; } start() { this.device.on(PACKET_REPORT, this.handlePacket); } stop() { this.device.off(PACKET_REPORT, this.handlePacket); } async sendCommandAsync(p) { p.serviceIndex = 1; await p.sendCmdAsync(this.device); } async startFlashAsync() { this.sessionId = Math.random() * 268435456 | 0; for (const d of this.classClients) { d.start(); log( `flashing ${d.device.shortId}; available flash=${d.flashSize / 1024}kb; page=${d.pageSize}b` ); } const setsession = Packet.jdpacked( 129 /* SetSession */, "u32", [this.sessionId] ); this.allPending(); for (let i = 0; i < BL_RETRIES; ++i) { for (const d of this.classClients) { if (d.pending) { if (d.lastStatus && d.lastStatus.getNumber(11 /* UInt32LE */, 0) == this.sessionId) { d.pending = false; } else { d.lastStatus = null; log(`set session ${this.sessionId} on ${d.device}`); await d.sendCommandAsync(setsession); } await this.bus.delay(BL_SESSION_DELAY); } } if (this.numPending() == 0) break; await this.waitForStatusAsync(); } if (this.numPending()) throw new Error("Can't set session id"); } async endFlashAsync() { for (const f of this.classClients) { await this.bus.delay(10); await f.device.reset(); } } allPending() { for (const c of this.classClients) { c.pending = true; c.lastStatus = null; } } numPending() { let num = 0; for (const c of this.classClients) if (c.pending) num++; return num; } async waitForStatusAsync() { for (let i = 0; i < 100; ++i) { if (this.classClients.every((c) => c.lastStatus != null)) break; await this.bus.delay(5); } } async flashPage(page) { const pageAddr = page.targetAddress; const pageSize = this.pageSize; const numSubpage = (pageSize + BL_SUBPAGE_SIZE - 1) / BL_SUBPAGE_SIZE | 0; log( `flash ${prettySize(this.pageSize)} at ${(pageAddr & 16777215).toString(16)}` ); if (page.data.length != this.pageSize) throw new Error("invalid page size"); for (const f of this.classClients) f.lastStatus = null; this.allPending(); for (let i = 0; i < BL_RETRIES; ++i) { log(` attempt ${i}`); let currSubpage = 0; for (let suboff = 0; suboff < pageSize; suboff += BL_SUBPAGE_SIZE) { let sz = BL_SUBPAGE_SIZE; if (suboff + sz > pageSize) sz = pageSize - suboff; log( `send sub page ${currSubpage}/${numSubpage - 1} at ${suboff.toString(16)}[${sz}]` ); const hd = jdpack("u32 u16 u8 u8 u32 u32 u32 u32 u32", [ pageAddr, suboff, currSubpage++, numSubpage - 1, this.sessionId, 0, 0, 0, 0 ]); assert(hd.length == 4 * 7); const p = Packet.from( 128 /* PageData */, bufferConcat(hd, page.data.slice(suboff, suboff + sz)) ); if (this.classClients.length > 1 && (i == 0 || currSubpage < numSubpage)) await p.sendAsMultiCommandAsync(this.bus, SRV_BOOTLOADER); else { for (const f of this.classClients) if (f.pending) { f.lastStatus = null; await f.sendCommandAsync(p); } } await this.bus.delay(BL_PAGE_DELAY); } await this.waitForStatusAsync(); for (const f of this.classClients) { if (f.pending) { let err = ""; if (f.lastStatus) { const [session_id, page_error, pageAddrR] = jdunpack(f.lastStatus.data, "u32 u32 u32"); if (session_id != this.sessionId) err = "invalid session_id"; else if (pageAddrR != pageAddr) err = "invalid page address"; else if (page_error) err = "err: " + (BootloaderError[page_error] || page_error); } else { err = "timeout"; } if (err) { f.lastStatus = null; log(`retry ${f.device}: ${err}`); } else { f.pending = false; } } } if (this.numPending() == 0) { log( `page ${pageAddr & 16777215} done, ${i}/${BL_RETRIES} retries` ); return; } } throw new Error("too many retries"); } async flashFirmwareBlob(fw) { const waitCycles = 15; this.progressTotal = fw.pages.length + waitCycles + 3; this.progressIndex = 0; const prog = () => { this.updater.emit(PROGRESS, this.progress); this.progressIndex++; }; try { this.updater.emit(CHANGE); prog(); await this.startFlashAsync(); prog(); for (const page of fw.pages) { await this.flashPage(page); prog(); } } finally { try { await this.endFlashAsync(); prog(); for (let i = 0; i < waitCycles; ++i) { await this.bus.delay(150); prog(); } } finally { for (const d of this.classClients) { d.stop(); } } this.updater.emit(CHANGE); } } }; var UF2_MAGIC_START0 = 171066965; var UF2_MAGIC_START1 = 2656915799; var UF2_MAGIC_END = 179400496; function parseUF2Firmware(uf2, store) { const blobs = []; let currBlob; for (let off = 0; off < uf2.length; off += 512) { const header = uf2.slice(off, off + 32); const [ magic0, magic1, flags, trgaddr, payloadSize, blkNo, numBlocks, familyID ] = bufferToArray(header, 11 /* UInt32LE */); if (magic0 != UF2_MAGIC_START0 || magic1 != UF2_MAGIC_START1 || getNumber(uf2, 11 /* UInt32LE */, off + 512 - 4) != UF2_MAGIC_END) throw new Error("invalid UF2"); if (blkNo == 0) { flush(); currBlob = { pages: [], productIdentifier: familyID, version: "", pageSize: 1024, name: "FW " + familyID.toString(16), store }; } if (flags & 32768) parseExtTags(uf2.slice(off + 32 + payloadSize, off + 512)); const pageSize = currBlob.pageSize || 1024; let currPage = currBlob.pages[currBlob.pages.length - 1]; if (!currPage || !(currPage.targetAddress <= trgaddr && trgaddr < currPage.targetAddress + pageSize)) { currPage = { targetAddress: trgaddr & ~(pageSize - 1), data: new Uint8Array(pageSize) }; currPage.data.fill(255); currBlob.pages.push(currPage); } currPage.data.set( uf2.slice(off + 32, off + 32 + payloadSize), trgaddr - currPage.targetAddress ); } flush(); return blobs; function flush() { if (currBlob) blobs.push(currBlob); } function parseExtTags(buf) { let sz = 0; for (let i = 0; i < buf.length; i += sz) { sz = buf[i]; if (sz == 0) break; const desig = getNumber(buf, 11 /* UInt32LE */, i) >>> 8; for (const key of Object.keys(uf2ExtTags)) { const tg = uf2ExtTags[key]; if (desig == Math.abs(tg)) { let v; if (tg < 0) { v = bufferToString(buf.slice(i + 4, i + sz)); } else { v = getNumber(buf, 11 /* UInt32LE */, i + 4); } const cbany = currBlob; cbany[key] = v; break; } } sz = sz + 3 & ~3; } } } async function parseFirmwareFile(blob, store) { const data = await readBlobToUint8Array(blob); const buf = new Uint8Array(data); const uf2Blobs = parseUF2Firmware(buf, store); return uf2Blobs; } async function createFlashers(updater, bootloaderDeviceIds) { const bus = updater.bus; const flashers = []; const numTries = 10; const tryDelay = 10; const handlePkt = (p) => { if (p.serviceIndex == 1 && p.serviceCommand == CMD_ADVERTISEMENT_DATA && p.getNumber(11 /* UInt32LE */, 0) == SRV_BOOTLOADER) { if (!flashers.find((f) => f.device.deviceId == p.deviceIdentifier)) { log(`new flasher`); flashers.push(new FlashClient(updater, p)); } } }; try { bus.on(PACKET_REPORT, handlePkt); for (let i = 0; i < numTries; ++i) { if (bootloaderDeviceIds?.length > 1) { const bl_announce = Packet.onlyHeader(CMD_ADVERTISEMENT_DATA); await bl_announce.sendAsMultiCommandAsync(bus, SRV_BOOTLOADER); await bus.delay(tryDelay); } else { for (const id of bootloaderDeviceIds) { const bl_announce = Packet.onlyHeader( CMD_ADVERTISEMENT_DATA ); bl_announce.serviceIndex = 1; bl_announce.deviceIdentifier = id; bl_announce.isCommand = true; await bus.sendPacketAsync(bl_announce); } } await bus.delay(tryDelay); } } finally { bus.off(PACKET_REPORT, handlePkt); } return flashers; } function updateApplicable(dev, blob) { return dev && blob && dev.bootloaderProductIdentifier == blob.productIdentifier && dev.version !== blob.version; } async function sendStayInBootloaderCommand(bus) { const bl_announce = Packet.onlyHeader(0 /* Info */); await bl_announce.sendAsMultiCommandAsync(bus, SRV_BOOTLOADER); } // src/jdom/servers/registerserver.ts function defaultFieldPayload(specification) { let r = void 0; switch (specification.type) { case "bool": r = 0; break; case "i8": case "i16": case "i32": case "u8": case "u16": case "u32": { const min = pick( specification.typicalMin, specification.absoluteMin, void 0 ); const max = pick( specification.typicalMax, specification.absoluteMax, void 0 ); if (max !== void 0 && min !== void 0) r = (max + min) / 2; else r = 0; break; } case "bytes": { r = new Uint8Array(0); break; } case "string": case "string0": { r = ""; break; } } if (/^(u0|i1)\.\d+$/.test(specification.type)) r = 0; return r; } function defaultPayload(specification) { const { fields } = specification; const rs = fields.map(defaultFieldPayload); return rs; } var JDRegisterServer = class extends JDEventSource { constructor(service, identifier, defaultValue) { super(); this.service = service; this.identifier = identifier; this.skipBoundaryCheck = false; this.skipErrorInjection = false; this.allowLargeFrames = false; const serviceSpecification = this.service.specification; this.specification = serviceSpecification.packets.find( (pkt) => isRegister2(pkt) && pkt.identifier === this.identifier ); let v = defaultValue; if (!v && !this.specification.optional) v = defaultPayload(this.specification); if (v !== void 0 && !v.some((vi) => vi === void 0)) { this.data = jdpack(this.packFormat, v); } this.resetData = this.data?.slice(0); this.skipBoundaryCheck = !this.specification?.fields.some( (field) => isSet(field.absoluteMin) || isSet(field.absoluteMax) ); } get packFormat() { return this.specification.packFormat; } values() { return jdunpack(this.data, this.packFormat); } normalize(values2) { if (!this.skipBoundaryCheck) { this.specification?.fields.forEach((field, fieldi) => { if (field.isSimpleType) { let value = values2[fieldi]; const min = field.absoluteMin; if (min !== void 0) value = Math.max(min, value); const max = field.absoluteMax; if (max !== void 0) value = Math.min(max, value); values2[fieldi] = value; } }); } this.emit(PACKET_DATA_NORMALIZE, values2); } shouldNormalize() { return !this.skipBoundaryCheck || this.listenerCount(PACKET_DATA_NORMALIZE); } /** * Sets the value on the register * @param values values to set * @param skipChangeEvent true to avoid emitting CHANGE * @returns true if values changed; false otherwise */ setValues(values2, skipChangeEvent) { if (this.readOnly) return false; if (this.shouldNormalize()) this.normalize(values2); if (this.valueProcessor) values2 = this.valueProcessor(values2); const d = jdpack(this.packFormat, values2); if (!bufferEq(this.data, d)) { this.data = d; if (!skipChangeEvent) this.emit(CHANGE); return true; } return false; } reset() { this.data = this.resetData?.slice(0); } async sendGetAsync() { this.emit(REGISTER_PRE_GET); let d = this.data; if (!d) return; if (this.allowLargeFrames && d.length > JD_SERIAL_MAX_PAYLOAD_SIZE) { d = new Uint8Array(0); } const error = !this.skipErrorInjection && this.errorRegister?.values()[0]; if (error && !isNaN(error)) { const vs = this.values(); for (let i = 0; i < vs.length; ++i) { vs[i] += Math.random() * error; } d = jdpack(this.packFormat, vs); } await this.service.sendPacketAsync( Packet.from(this.identifier | CMD_GET_REG, d) ); } handlePacket(pkt) { if (this.identifier !== pkt.registerIdentifier) return false; if (pkt.isRegisterGet) { this.sendGetAsync(); } else if (this.identifier >> 8 !== 1) { let changed = false; let d = pkt.data; if (this.shouldNormalize() || this.valueProcessor) { try { let values2 = jdunpack(d, this.packFormat); if (this.shouldNormalize()) this.normalize(values2); if (this.valueProcessor) values2 = this.valueProcessor(values2); d = jdpack(this.packFormat, values2); } catch (e) { this.emit(PACKET_INVALID_DATA, pkt); } } if (!bufferEq(this.data, d)) { this.data = d; changed = true; } this.lastSetTime = this.service.timestamp; this.emit(REPORT_RECEIVE); if (changed) this.emit(CHANGE); } return true; } }; // src/jdom/servers/serviceserver.ts var CALIBRATION_DELAY = 5e3; var JDServiceServer = class extends JDEventSource { constructor(serviceClass2, options) { super(); this.serviceClass = serviceClass2; this.serviceIndex = -1; this._registers = []; this.commands = {}; this._locked = false; const { instanceName, variant, valueValues, intensityValues, intensityProcessor, registerValues, clientVariant, isActive } = options || {}; this.specification = serviceSpecificationFromClassIdentifier( this.serviceClass ); this.statusCode = this.addRegister( 259 /* StatusCode */, [0 /* Ready */, 0] ); if (valueValues) this.addRegister(2 /* Value */, valueValues); if (intensityValues) { const intensity = this.addRegister( 1 /* Intensity */, intensityValues ); if (intensityProcessor) intensity.valueProcessor = intensityProcessor; if (isActive) intensity.on(CHANGE, () => { const ev = isActive(intensity.values()); if (ev !== void 0) this.sendEvent( isActive(intensity.values()) ? 1 /* Active */ : 2 /* Inactive */ ); }); } if (variant) this.addRegister(263 /* Variant */, [variant]); if (clientVariant) this.addRegister(9 /* ClientVariant */, [clientVariant]); this.instanceName = this.addRegister(265 /* InstanceName */, [ instanceName || "" ]); registerValues?.forEach( ({ code, values: values2 }) => this.addRegister(code, values2) ); this.statusCode.on( CHANGE, () => this.sendEvent(4 /* StatusCodeChanged */, this.statusCode.data) ); if (this.specification.packets.find( (pkt) => pkt.kind === "command" && pkt.identifier === 2 /* Calibrate */ )) { this.addCommand( 2 /* Calibrate */, this.handleCalibrate.bind(this) ); this.statusCode.setValues( [100 /* CalibrationNeeded */, 0], true ); } this.handleTwinPacket = this.handleTwinPacket.bind(this); } get device() { return this._device; } set device(value) { if (this._device !== value) { this._device = value; this.emit(DEVICE_CHANGE); this.emit(CHANGE); } } get twin() { return this._twin; } set twin(service) { if (service === this._twin) return; if (this._twin) { this._twin.off(PACKET_RECEIVE, this.handleTwinPacket); this._twin.off(PACKET_SEND, this.handleTwinPacket); this._twinCleanup.forEach((tw) => tw()); } this._twin = service; this._twinCleanup = service ? [] : void 0; if (this._twin) { this._twin.on(PACKET_RECEIVE, this.handleTwinPacket); this._twin.on(PACKET_SEND, this.handleTwinPacket); this._twin.registers().forEach((twinReg) => { const reg = this.register(twinReg.code); if (reg) { reg?.setValues(twinReg.unpackedValue); this._twinCleanup.push( twinReg.subscribe( REPORT_UPDATE, () => reg.setValues(twinReg.unpackedValue) ) ); } }); } this.emit(CHANGE); } handleTwinPacket(pkt) { this.handlePacket(pkt); } get registers() { return this._registers.slice(0); } get timestamp() { const bus = this.device?.bus || this._twin?.device?.bus; return bus?.timestamp; } register(code) { return this._registers.find( (reg) => reg.identifier === code ); } addExistingRegister(reg) { this._registers.push(reg); return reg; } addRegister(identifier, defaultValue) { let reg = this._registers.find( (r) => r.identifier === identifier ); if (!reg && !this._locked) { if (!this.specification.packets.find( (pkt) => isRegister2(pkt) && pkt.identifier === identifier )) return void 0; reg = new JDRegisterServer(this, identifier, defaultValue); this._registers.push(reg); } return reg; } reset() { this.registers.forEach((reg) => reg.reset()); } /** * Locks the current set of registers */ lock() { this._locked = true; } addCommand(identifier, handler) { if (this._locked) console.error(`adding command to locked service`); this.commands[identifier] = handler; } async handlePacket(pkt) { if (pkt.isRegisterGet || pkt.isRegisterSet) { const rid = pkt.registerIdentifier; let reg = this._registers.find((r) => r.identifier === rid); if (!reg) { reg = this.addRegister(rid); } reg?.handlePacket(pkt); } else if (pkt.isCommand) { const cmd = this.commands[pkt.serviceCommand]; if (cmd) cmd(pkt); else if (cmd === void 0) console.debug(`ignored command`, { pkt }); } } async sendPacketAsync(pkt) { if (this.twin) return; pkt.serviceIndex = this.serviceIndex; await this.device.sendPacketAsync(pkt); } async sendEvent(eventCode, data) { if (this.twin) return; const { device } = this; if (!device) return; const { bus } = device; if (!bus) return; const now = bus.timestamp; const cmd = device.createEventCmd(eventCode); const pkt = Packet.from(cmd, data || new Uint8Array(0)); await this.sendPacketAsync(pkt); device.delayedSend(pkt, now + 20); device.delayedSend(pkt, now + 100); } async handleCalibrate() { const [status] = this.statusCode.values(); if (status !== 0 /* Ready */) return; this.calibrate(); } processLargeFrame(command, data) { this.emit(FRAME_PROCESS_LARGE, command, data); } async calibrate() { this.statusCode.setValues([2 /* Calibrating */, 0]); await this.device.bus.delay(CALIBRATION_DELAY); this.statusCode.setValues([0 /* Ready */, 0]); } }; // src/servers/sensorserver.ts var SensorServer = class extends JDServiceServer { constructor(serviceClass2, options) { super(serviceClass2, options); this.serviceClass = serviceClass2; this.lastStream = 0; this.lastErrorReadingChanged = false; const { readingValues, streamingInterval, preferredStreamingInterval, readingError } = options || {}; this.reading = this.addRegister( 257 /* Reading */, readingValues ); this.streamingSamples = this.addRegister( 3 /* StreamingSamples */ ); this.streamingInterval = this.addRegister( 4 /* StreamingInterval */, [ streamingInterval || preferredStreamingInterval || this.reading.specification.preferredInterval || STREAMING_DEFAULT_INTERVAL ] ); if (preferredStreamingInterval !== void 0) this.preferredStreamingInterval = this.addRegister( 258 /* StreamingPreferredInterval */, [preferredStreamingInterval] ); if (readingError !== void 0) { this.readingError = this.addRegister( 262 /* ReadingError */, readingError ); this.reading.errorRegister = this.readingError; this.readingError.on( CHANGE, () => this.lastErrorReadingChanged = true ); } this.on(REFRESH, this.refreshRegisters.bind(this)); } refreshRegisters() { const [samples] = this.streamingSamples.values(); if (samples <= 0 || !this.reading.data) return; let interval = this.streamingInterval?.values()?.[0]; if (interval === void 0) interval = this.preferredStreamingInterval?.values()?.[0]; if (interval === void 0) interval = this.reading.specification.preferredInterval; if (interval === void 0) interval = STREAMING_DEFAULT_INTERVAL; const now = this.device.bus.timestamp; if (now - this.lastStream > interval) { this.lastStream = now; this.streamingSamples.setValues([samples - 1]); this.reading.sendGetAsync(); this.emit(READING_SENT); if (this.lastErrorReadingChanged) { this.readingError?.sendGetAsync(); this.lastErrorReadingChanged = false; } } } }; // src/servers/realtimeclockserver.ts function dateToClock(n) { const year = n.getFullYear(); const month = n.getMonth() + 1; const dayOfMonth = n.getDate(); const dayOfWeek = n.getDay(); const hour = n.getHours(); const min = n.getMinutes(); const sec = n.getSeconds(); return [year, month, dayOfMonth, dayOfWeek, hour, min, sec]; } var RealTimeClockServer = class extends SensorServer { constructor() { super(SRV_REAL_TIME_CLOCK, { readingValues: dateToClock(new Date()), variant: 1 /* Computer */, streamingInterval: 1e3 }); this.lastSecond = 0; this.drift = this.addRegister(384 /* Drift */, [0]); this.precision = this.addRegister( 385 /* Precision */, [0] ); this.addCommand(128 /* SetTime */, this.handleSetTime.bind(this)); this.on(REFRESH, this.refreshTime.bind(this)); } static async syncTime(bus) { const values2 = dateToClock(new Date()); const pkt = Packet.jdpacked( 128 /* SetTime */, "u16 u8 u8 u8 u8 u8 u8", values2 ); await pkt.sendAsMultiCommandAsync(bus, SRV_REAL_TIME_CLOCK); } handleSetTime(pkt) { } refreshTime() { const d = new Date(); const s = d.getSeconds(); if (s !== this.lastSecond) { const r = dateToClock(d); this.reading.setValues(r); this.lastSecond = s; } } }; // src/jdom/transport/transport.ts var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => { ConnectionState2["Connected"] = "connected"; ConnectionState2["Connecting"] = "connecting"; ConnectionState2["Disconnecting"] = "disconnecting"; ConnectionState2["Disconnected"] = "disconnected"; return ConnectionState2; })(ConnectionState || {}); var Transport = class extends JDEventSource { constructor(type, options) { super(); this.type = type; this.disposed = false; this._connectionState = "disconnected" /* Disconnected */; this.checkPulse = this.checkPulse.bind(this); this._checkPulse = !!options?.checkPulse; this._disconnectOnError = !!options?.disconnectOnError; this._cleanups = [ options?.connectObservable?.subscribe({ next: async () => { const { bus } = this; if (Flags.diagnostics) console.debug( `${this.type}: device detected, connect ${bus?.autoConnect ? "auto" : "manual"}` ); if (bus?.disconnected && this.bus?.autoConnect) { await delay(TRANSPORT_CONNECT_RETRY_DELAY); if (bus?.disconnected && bus?.autoConnect) { if (typeof document === "undefined" || // Node.js document.visibilityState === "visible") { this.connect(true); } } } } })?.unsubscribe, options?.disconnectObservable?.subscribe({ next: () => { this.disconnect(); } })?.unsubscribe ].filter((c) => !!c); } get pulseTimeout() { return 1e3; } get bus() { return this._bus; } description() { return void 0; } async disconnectBus() { if (this._bus) { this._bus.off(SELF_ANNOUNCE, this.checkPulse); await this.disconnect(); } this._bus = void 0; } setBus(bus) { assert(!this._bus); assert(!!bus); this._bus = bus; if (this._bus && this._checkPulse) this._bus.on(SELF_ANNOUNCE, this.checkPulse); } /** * Gets the bus connection state. */ get connectionState() { return this._connectionState; } setConnectionState(state) { if (this._connectionState !== state) { this._connectionState = state; this._connectionTime = state === "connected" /* Connected */ ? this.bus?.timestamp : void 0; this._lastReceivedTime = void 0; this.emit(CONNECTION_STATE, this._connectionState); this.bus?.emit(CONNECTION_STATE, this); switch (this._connectionState) { case "connected" /* Connected */: this.emit(CONNECT); break; case "connecting" /* Connecting */: this.emit(CONNECTING); break; case "disconnecting" /* Disconnecting */: this.emit(DISCONNECTING); break; case "disconnected" /* Disconnected */: this.emit(DISCONNECT); break; } this.emit(CHANGE); this.bus?.emit(CHANGE); } } get connecting() { return this.connectionState == "connecting" /* Connecting */; } get disconnecting() { return this.connectionState == "disconnecting" /* Disconnecting */; } get connected() { return this._connectionState == "connected" /* Connected */; } get disconnected() { return this._connectionState == "disconnected" /* Disconnected */; } async checkPulse() { assert(this._checkPulse); const { bus, _lastPulse } = this; const n = Date.now(); this._lastPulse = n; if (_lastPulse) { const d = n - _lastPulse; if (d < 400 || d > 700) return; } if (!this.connected || !bus) return; if (bus.safeBoot) return; const devices = bus.devices(); if (devices?.some((dev) => dev.firmwareUpdater)) return; const t = bus.timestamp - (this._lastReceivedTime || this._connectionTime); if (t > this.pulseTimeout) { this.emit(LOST); if (Flags.diagnostics) console.debug( `${this.type}: lost connection with device t=${bus.timestamp}` ); if (this._lastReceivedTime !== void 0) { await this.reconnect(); if (this.connectionState == "disconnected" /* Disconnected */) { await delay(500); await this.reconnect(); } } else await this.disconnect(true); } } async sendPacketWhenConnectedAsync(frame) { if (this.connected) await this.transportSendPacketAsync(frame); else this.emit(FRAME_SEND_DISCONNECT, frame); } connect(background) { if (Flags.diagnostics) console.debug( `${this.type}: connect ${background ? `(background)` : ""}` ); if (this.disposed) throw new Error("attempted to connect to a disposed transport"); if (this.connectionState == "connected" /* Connected */) { if (Flags.diagnostics) console.debug(`${this.type}: already connected`); return Promise.resolve(); } if (!this._connectPromise) { if (this._disconnectPromise) { if (Flags.diagnostics) console.debug( `${this.type}: queuing connect after disconnecting` ); const p = this._disconnectPromise; this._disconnectPromise = void 0; this._connectPromise = p.then(() => this.connect()); } else { console.debug(`${this.type}: connecting`); this._connectPromise = Promise.resolve(); this.setConnectionState("connecting" /* Connecting */); this._connectPromise = this._connectPromise.then( () => this.transportConnectAsync(background) ); const p = this._connectPromise = this._connectPromise.then(() => { if (p == this._connectPromise) { this._connectPromise = void 0; this.setConnectionState("connected" /* Connected */); } else { console.debug( `${this.type}: connection aborted in flight`, { state: this._connectionState, old: this._connectPromise, new: p } ); } }).catch((e) => { if (p == this._connectPromise) { this._connectPromise = void 0; this.setConnectionState( "disconnected" /* Disconnected */ ); if (!background) this.errorHandler(CONNECT, e); else if (Flags.diagnostics) console.debug( `${this.type}: background connect failed, ${e.message}` ); } else { if (Flags.diagnostics) console.debug( `${this.type}: connection error aborted in flight` ); } }); } } else { if (Flags.diagnostics) console.debug(`${this.type}: connect with existing promise`); } return this._connectPromise; } disconnect(background) { if (this.connectionState == "disconnected" /* Disconnected */) return Promise.resolve(); if (!this._disconnectPromise) { if (this._connectPromise) { if (Flags.diagnostics) console.debug( `${this.type}: cancelling connection and disconnect` ); this._connectPromise = void 0; } console.debug(`${this.type}: disconnecting`); this._disconnectPromise = Promise.resolve(); this.setConnectionState("disconnecting" /* Disconnecting */); this._disconnectPromise = this._disconnectPromise.then( () => this.transportDisconnectAsync(background) ); this._disconnectPromise = this._disconnectPromise.catch((e) => { this._disconnectPromise = void 0; this.errorHandler(DISCONNECT, e); }).finally(() => { this._disconnectPromise = void 0; this.setConnectionState("disconnected" /* Disconnected */); }); } else { if (Flags.diagnostics) console.debug(`${this.type}: disconnect with existing promise`); } return this._disconnectPromise; } async reconnect() { if (Flags.diagnostics) console.debug(`${this.type}: reconnect`); await this.disconnect(true); await this.connect(true); } handleFrame(payload, skipCrc = false) { const { bus } = this; if (!bus) return; const { timestamp: timestamp2 } = bus; this._lastReceivedTime = timestamp2; this.bus.processFrame(payload, this.type, skipCrc); } handleLog(line) { this.emit(LOG, line); } // eslint-disable-next-line @typescript-eslint/no-explicit-any errorHandler(context, exception) { if (!isCancelError(exception)) { this.emit(ERROR, { context, exception }); this.bus?.emit(TRANSPORT_ERROR, { transport: this, context, exception }); } this.emit(CHANGE); if (this._disconnectOnError) { this.disconnect(true); } } sendSideData(data) { throw new Error(`side data not supported on ${this}`); } dispose() { this.emit(DISPOSE); this.disposed = true; this._cleanups.forEach((c) => c()); this._cleanups = []; } }; // src/jdom/busstats.ts var BusStatsMonitor = class extends JDEventSource { /** * @internal */ constructor(bus) { super(); this.bus = bus; this._prev = Array(4).fill(0).map(() => ({ packets: 0, announce: 0, acks: 0, bytes: 0 })); this._previ = 0; this._temp = { packets: 0, announce: 0, acks: 0, bytes: 0 }; bus.on(PACKET_SEND, this.handlePacketSend.bind(this)); bus.on(PACKET_PROCESS, this.handlePacketProcess.bind(this)); bus.on(SELF_ANNOUNCE, this.handleSelfAnnounce.bind(this)); } /** * Computes the current packet statistics of the bus */ get current() { const r = { packets: 0, announce: 0, acks: 0, bytes: 0 }; const n = this._prev.length; for (let i = 0; i < this._prev.length; ++i) { const p = this._prev[i]; r.packets += p.packets; r.announce += p.announce; r.acks += p.acks; r.bytes += p.bytes; } const n2 = n / 2; r.packets /= n2; r.announce /= n2; r.acks /= n2; r.bytes /= n2; return { devices: this.bus.devices({ ignoreInfrastructure: true }).length, simulators: this.bus.serviceProviders().length, transport: this.bus.transports.find( (transport) => transport.connected )?.type, ...r }; } accumulate(pkt) { this._temp.packets++; this._temp.bytes += (pkt.header?.length || 0) + (pkt.data?.length || 0); if (pkt.isAnnounce) this._temp.announce++; if (pkt.isCRCAck) this._temp.acks++; } handleSelfAnnounce() { const changed = JSON.stringify(this._prev) !== JSON.stringify(this._temp); this._prev[this._previ] = this._temp; this._previ = (this._previ + 1) % this._prev.length; this._temp = { packets: 0, announce: 0, acks: 0, bytes: 0 }; if (changed) this.emit(CHANGE); } handlePacketSend(pkt) { this.accumulate(pkt); } handlePacketProcess(pkt) { this.accumulate(pkt); } }; // src/jdom/servers/controlserver.ts var ControlServer = class extends JDServiceServer { constructor(options) { super(SRV_CONTROL); const { resetIn, deviceDescription } = options || {}; this.startTime = Date.now(); this.deviceDescription = this.addRegister( 384 /* DeviceDescription */, [deviceDescription || "Simulated"] ); this.mcuTemperature = this.addRegister( 386 /* McuTemperature */, [25] ); this.uptime = this.addRegister(390 /* Uptime */); if (resetIn) this.resetIn = this.addRegister(128 /* ResetIn */, [0]); this.addRegister(389 /* FirmwareVersion */, ["0.0.0"]); this.addRegister(385 /* ProductIdentifier */, [0]); this.addRegister(388 /* BootloaderProductIdentifier */, [0]); this.addCommand(0 /* Services */, this.announce.bind(this)); this.addCommand(129 /* Identify */, this.identify.bind(this)); this.addCommand(130 /* Reset */, this.handleReset.bind(this)); this.addCommand(128 /* Noop */, null); } async announce() { const pkt = Packet.jdpacked( 0 /* Services */, "u16 u8 x[1] u32[]", [ this.device.restartCounter | 32 /* StatusLightRgbNoFade */ | 256 /* SupportsACK */, this.device.packetCount + 1, this.device.services().slice(1).map((srv) => srv.serviceClass) ] ); await this.sendPacketAsync(pkt); this.uptime.setValues([(Date.now() - this.startTime) * 100], true); if (this.resetIn) { const [resetIn] = this.resetIn.values(); if (resetIn) { const resetTimestamp = resetIn / 1e3 + this.resetIn.lastSetTime; if (resetTimestamp < this.device.bus.timestamp) { console.debug(`${this} reset in expired`, { resetIn, lastSet: this.resetIn.lastSetTime, resetTimestamp }); this.device.reset(); } } } } async identify() { this.emit(IDENTIFY); } handleReset() { this.startTime = Date.now(); this.device.reset(); } }; // src/jdom/servers/serviceprovider.ts var JDServiceProvider = class extends JDEventSource { constructor(template, deviceId) { super(); this.template = template; this.deviceId = deviceId; if (!this.deviceId) this.deviceId = randomDeviceId(); this.shortId = shortDeviceId(this.deviceId); this.handleSelfAnnounce = this.handleSelfAnnounce.bind(this); this.handlePacket = this.handlePacket.bind(this); } get bus() { return this._bus; } set bus(value) { if (value !== this._bus) { this.stop(); this._bus = value; if (this._bus) this.start(); this.emit(CHANGE); } } start() { if (this._bus) { this._bus.on(SELF_ANNOUNCE, this.handleSelfAnnounce); this._bus.on(PACKET_PROCESS, this.handlePacket); } } stop() { if (this._bus) { this._bus.off(SELF_ANNOUNCE, this.handleSelfAnnounce); this._bus.off(PACKET_PROCESS, this.handlePacket); this._bus = void 0; } } handleSelfAnnounce() { this.emit(SELF_ANNOUNCE); } }; // src/jdom/servers/serverserviceprovider.ts var JDServerServiceProvider = class extends JDServiceProvider { constructor(template, services, options) { super(template, options?.deviceId); this._restartCounter = 0; this._packetCount = 0; this._eventCounter = void 0; this.controlService = new ControlServer(options); this._services = []; this.updateServices(services); this.on(REFRESH, this.refreshRegisters.bind(this)); } updateServices(services) { this._services?.slice(1).forEach((srv) => srv.device = void 0); this._services = [this.controlService, ...services]; if (this._services.length >= MAX_SERVICES_LENGTH) { this.emit( ERROR, `too many services (${this._services.length}) > ${MAX_SERVICES_LENGTH}` ); console.warn(`jacdac: dropping services to ${MAX_SERVICES_LENGTH}`); this._services = this._services.slice(0, MAX_SERVICES_LENGTH); } this._services.forEach((srv, i) => { srv.device = this; srv.serviceIndex = i; }); this.emit(CHANGE); } removeService(service) { if (service?.device !== this) return; const newServices = this._services.slice(1); const index = newServices.indexOf(service); if (index > -1) { newServices.splice(index, 1); this.updateServices(newServices); } } start() { super.start(); this._packetCount = 0; this.emit(CONNECT); } stop() { this.emit(DISCONNECT); this._delayedPackets = void 0; super.stop(); } handleSelfAnnounce() { super.handleSelfAnnounce(); if (this._restartCounter < 15) this._restartCounter++; this.controlService.announce(); const activeServices = this.services().filter( (srv) => !isBufferEmpty(srv.statusCode.data) ); activeServices.forEach((srv) => srv.statusCode.sendGetAsync()); this._packetCount = 0; } get restartCounter() { return this._restartCounter; } get packetCount() { return this._packetCount; } services() { return this._services.slice(0); } service(serviceIndex) { return serviceIndex !== void 0 && this._services[serviceIndex]; } toString() { return `host ${this.shortId}`; } get eventCounter() { return this._eventCounter; } createEventCmd(evCode) { if (!this._eventCounter) this._eventCounter = 0; this._eventCounter = this._eventCounter + 1 & CMD_EVENT_COUNTER_MASK; if (evCode >> 8) throw new Error("invalid event code"); return CMD_EVENT_MASK | this._eventCounter << CMD_EVENT_COUNTER_POS | evCode; } async sendPacketAsync(pkt) { if (!this.bus) return Promise.resolve(); this._packetCount++; pkt.deviceIdentifier = this.deviceId; return pkt.sendCoreAsync(this.bus); } delayedSend(pkt, timestamp2) { if (!this._delayedPackets) { this._delayedPackets = []; setTimeout(this.processDelayedPackets.bind(this), 10); } const dp = { timestamp: timestamp2, pkt }; this._delayedPackets.push(dp); this._delayedPackets.sort((l, r) => -l.timestamp + r.timestamp); } processDelayedPackets() { while (this._delayedPackets?.length) { const { timestamp: timestamp2, pkt } = this._delayedPackets[0]; if (timestamp2 > this.bus.timestamp) break; this._delayedPackets.shift(); try { this.sendPacketAsync(pkt); } catch (e) { this._delayedPackets = void 0; throw e; } } if (!this._delayedPackets?.length) this._delayedPackets = void 0; else setTimeout(this.processDelayedPackets.bind(this), 10); } handlePacket(pkt) { const devIdMatch = pkt.deviceIdentifier == this.deviceId; if (pkt.requiresAck && devIdMatch) { pkt.requiresAck = false; const crc2 = pkt.crc; const ack = Packet.onlyHeader(crc2); ack.serviceIndex = JD_SERVICE_INDEX_CRC_ACK; this.sendPacketAsync(ack); } if (pkt.isMultiCommand) { if (!pkt.isCommand) return; const multiCommandClass = pkt.serviceClass; for (const h of this._services) { if (h.serviceClass == multiCommandClass) { const npkt = pkt.cloneForDevice( this.deviceId, h.serviceIndex ); h.handlePacket(npkt); } } } else if (devIdMatch) { if (!pkt.isCommand) return; const h = this._services[pkt.serviceIndex]; if (h) { h.handlePacket(pkt); } } else { if (pkt.isCommand) return; } } refreshRegisters() { this._services.forEach((srv) => srv.emit(REFRESH)); } reset() { this._restartCounter = 0; this._packetCount = 0; this._services?.forEach((srv) => srv.reset()); this.emit(RESET); } }; // src/jdom/client.ts var JDClient = class extends JDEventSource { constructor() { super(); this.unsubscribers = []; this.unmounted = false; } // eslint-disable-next-line @typescript-eslint/no-explicit-any log(msg, arg) { if (arg) console.debug(msg, arg); else console.debug(msg); } mount(unsubscribe) { this.unmounted = false; if (unsubscribe && this.unsubscribers.indexOf(unsubscribe) < 0) this.unsubscribers.push(unsubscribe); return unsubscribe; } unmount() { const us = this.unsubscribers; this.unsubscribers = []; us.forEach((u) => u()); this.unmounted = true; } }; // src/jdom/pipes.ts var OutPipe = class { constructor(device, port, hosted) { this.device = device; this.port = port; this.hosted = hosted; this._count = 0; } static from(bus, pkt, hosted) { const [idbuf, port] = pkt.jdunpack("b[8] u16"); const id = toHex2(idbuf); const dev = bus.device(id, false, pkt); return new OutPipe(dev, port, hosted); } get count() { return this._count; } get isOpen() { return this.device != null; } send(buf) { return this.sendData(buf, 0, false); } sendMeta(buf) { return this.sendData(buf, PIPE_METADATA_MASK, false); } async respondForEach(items, converter) { const n = items.length; for (let i = 0; i < n; ++i) { const item = items[i]; const data = converter(item); await this.send(data); } await this.close(); } async sendData(buf, flags, closing) { if (!this.device) { if (closing) return; else throwError("sending data over closed pipe"); } const cmd = this.port << PIPE_PORT_SHIFT | flags | this._count & PIPE_COUNTER_MASK; const pkt = Packet.from(cmd, buf); pkt.serviceIndex = JD_SERVICE_INDEX_PIPE; try { await this.device.sendPktWithAck(pkt); } catch (e) { this.free(); if (closing) console.debug(e); else throw e; } this._count++; } free() { this.device = null; this.port = null; } async close() { try { await this.sendData(new Uint8Array(0), PIPE_CLOSE_MASK, true); } finally { this.free(); } } async sendBytes(data) { const chunkSize = 224; for (let i = 0; i < data.length; i += chunkSize) { await this.send(data.slice(i, i + chunkSize)); } } static async sendBytes(service, cmd, data, onProgress) { const { device } = service; onProgress?.(0); const resp = await service.sendCmdAwaitResponseAsync( Packet.jdpacked(cmd, "u32", [data.length]), 3e3 ); onProgress?.(0.05); const [pipePort] = jdunpack(resp.data, "u16"); if (!pipePort) throw new Error("wrong port " + pipePort); const pipe = new OutPipe(device, pipePort); const chunkSize = 224; for (let i = 0; i < data.length; i += chunkSize) { await pipe.send(data.slice(i, i + chunkSize)); onProgress?.(0.05 + i / data.length * 0.9); } await pipe.close(); onProgress?.(1); } }; var InPipe = class extends JDClient { constructor(bus) { super(); this.bus = bus; this._count = 0; this._handlePacket = this._handlePacket.bind(this); this.allocPort(); this.mount( this.bus.selfDevice.subscribe(PACKET_RECEIVE, this._handlePacket) ); } get port() { return this._port; } get count() { return this._count; } get isOpen() { return this._port != null; } allocPort() { while (true) { this._port = 1 + randomUInt(511); const info = this.bus.selfDevice.port(this._port); if (!info.localPipe && !info.pipeType) { info.localPipe = this; break; } } } openCommand(cmd) { if (!this.isOpen) throwError("trying to access a closed pipe"); const b = jdpack("b[8] u16 u16", [ fromHex(this.bus.selfDeviceId), this._port, 0 ]); return Packet.from(cmd, b); } _handlePacket(pkt) { if (!pkt.isPipe) return; if (pkt.pipePort !== this._port) return; if ((pkt.serviceCommand & PIPE_COUNTER_MASK) == (this._count & PIPE_COUNTER_MASK)) { this._count++; this.emit(DATA, pkt); if (pkt.serviceCommand & PIPE_CLOSE_MASK) { this.close(); } } } close() { if (this._port == null) return; this.emit(CLOSE); this._port = null; this.bus.selfDevice.port(this._port).localPipe = void 0; this.unmount(); } }; var InPipeReader = class extends InPipe { constructor(bus) { super(bus); this.done = signal(); this.meta = []; this.output = []; this.mount( this.subscribe(DATA, (pkt) => { if (pkt.serviceCommand & PIPE_METADATA_MASK) this.meta.push(pkt); else this.output.push(pkt); }) ); this.mount(this.subscribe(CLOSE, this.done.signal)); } async readData(timeout = 500) { const r = await this.readAll(timeout); return r.output.map((p) => p.data).filter((b) => !!b?.length); } async readBytes(timeout = 500) { const data = await this.readData(timeout); const n = data.reduce((n2, b) => n2 + b.length, 0); const res = new Uint8Array(n); let i = 0; data.forEach((d) => { res.set(d, i); i += d.length; }); assert(i === n); return res; } async readAll(timeout = 500) { const res = await this.bus.withTimeout(timeout, this.done.signalled); if (!res) throwError("Timeout reading pipe: " + timeout + "ms", { code: ERROR_TIMEOUT }); return { meta: this.meta, output: this.output }; } }; // src/jdom/servers/protocoltestserver.ts var ProtocolTestServer = class extends JDServiceServer { constructor() { super(SRV_PROTO_TEST); this.init( 129 /* RwBool */, 385 /* RoBool */, 129 /* CBool */, 129 /* EBool */, false ); this.init( 131 /* RwI32 */, 387 /* RoI32 */, 131 /* CI32 */, 131 /* EI32 */, 0 ); this.init( 130 /* RwU32 */, 386 /* RoU32 */, 130 /* CU32 */, 130 /* EU32 */, 0 ); this.init( 132 /* RwString */, 388 /* RoString */, 132 /* CString */, 132 /* EString */, "" ); this.rwBytes = this.init( 133 /* RwBytes */, 389 /* RoBytes */, 133 /* CBytes */, 133 /* EBytes */, new Uint8Array(0) ); this.init( 134 /* RwI8U8U16I32 */, 390 /* RoI8U8U16I32 */, 134 /* CI8U8U16I32 */, 134 /* EI8U8U16I32 */, 0, 0, 0, 0 ); this.init( 135 /* RwU8String */, 391 /* RoU8String */, 135 /* CU8String */, 135 /* EU8String */, 0, "" ); this.addCommand( 144 /* CReportPipe */, this.handleReportPipe.bind(this) ); } init(rwi, roi, ci, ei, ...values2) { const rw = this.addRegister(rwi, values2); const ro = this.addRegister(roi, rw.values()); rw.on(CHANGE, () => { ro.setValues(rw.values()); this.sendEvent(ei, rw.data); }); this.addCommand( ci, (pkt) => rw.setValues(jdunpack(pkt.data, rw.specification.packFormat)) ); return rw; } async handleReportPipe(pkt) { const pipe = OutPipe.from(this.device.bus, pkt, true); await pipe.respondForEach(this.rwBytes.data, (b) => { const buf = new Uint8Array(1); buf[0] = b; return jdpack("b", [buf]); }); } }; // src/servers/buttonserver.ts var _ButtonServer = class extends SensorServer { constructor(instanceName, analog) { super(SRV_BUTTON, { instanceName, readingValues: [_ButtonServer.INACTIVE_VALUE], streamingInterval: 50 }); this.analog = this.addRegister(384 /* Analog */, [!!analog]); this.on(REFRESH, this.handleRefresh.bind(this)); } get threshold() { return this._threshold; } set threshold(value) { if (value !== this._threshold) { this._threshold = value; this.analog.setValues([!!this._threshold]); this.emit(CHANGE); } } isActive() { const [v] = this.reading.values(); const t = this.threshold?.values()?.[0] || 0.5; return v > t; } async handleRefresh() { const now = this.device.bus.timestamp; if (this.isActive()) { if (this._downTime === void 0) { this._downTime = now; this._nextHold = this._downTime + _ButtonServer.HOLD_TIME; await this.sendEvent(1 /* Down */); } else if (now > this._nextHold) { const time = now - this._downTime; this._nextHold = this.device.bus.timestamp + _ButtonServer.HOLD_TIME; await this.sendEvent( 129 /* Hold */, jdpack("u32", [time]) ); } } else { if (this._downTime !== void 0) { const time = now - this._downTime; this._downTime = void 0; this._nextHold = void 0; await this.sendEvent( 2 /* Up */, jdpack("u32", [time]) ); } } } async down() { this.reading.setValues([_ButtonServer.ACTIVE_VALUE]); } async up() { this.reading.setValues([_ButtonServer.INACTIVE_VALUE]); } }; var ButtonServer = _ButtonServer; ButtonServer.HOLD_TIME = 500; ButtonServer.INACTIVE_VALUE = 0; ButtonServer.ACTIVE_VALUE = 1; // src/servers/buzzerserver.ts function tonePayload(frequency, ms, volume) { const period = Math.round(1e6 / frequency); const duty = period * volume >> 11; return jdpack("u16 u16 u16", [period, duty, ms]); } var _BuzzerServer = class extends JDServiceServer { constructor(options) { super(SRV_BUZZER, options); this.volume = this.addRegister(1 /* Volume */, [0.2]); this.addCommand(128 /* PlayTone */, this.handlePlayTone.bind(this)); } handlePlayTone(pkt) { const [period, , duration] = jdunpack( pkt.data, "u16 u16 u16" ); const frequency = 1e6 / period; const [volume] = this.volume.values(); this.emit(_BuzzerServer.PLAY_TONE, { frequency, duration, volume }); } }; var BuzzerServer = _BuzzerServer; BuzzerServer.PLAY_TONE = "playTone"; // src/servers/characterscreenserver.ts var CharacterScreenServer = class extends JDServiceServer { constructor(options) { super(SRV_CHARACTER_SCREEN); const { message = "", rows = 2, columns = 16, variant, textDirection, brightness = 100 } = options || {}; this.message = this.addRegister(2 /* Message */, [ message ]); this.brightness = this.addRegister( 1 /* Brightness */, [brightness] ); this.rows = this.addRegister(384 /* Rows */, [rows]); this.columns = this.addRegister(385 /* Columns */, [ columns ]); this.variant = this.addRegister( 263 /* Variant */, [variant || 1 /* LCD */] ); this.message = this.addRegister(2 /* Message */, [ "" ]); this.textDirection = this.addRegister( 130 /* TextDirection */, [textDirection || 1 /* LeftToRight */] ); } }; // src/servers/gamepadserver.ts var GAMEPAD_DPAD_BUTTONS = 1 /* Left */ | 4 /* Right */ | 2 /* Up */ | 8 /* Down */; var GAMEPAD_ARCADE_BUTTONS = GAMEPAD_DPAD_BUTTONS | 16 /* A */ | 32 /* B */ | 64 /* Menu */ | 128 /* Select */ | 512 /* Exit */; var GAMEPAD_DPAD_A_BUTTONS = GAMEPAD_DPAD_BUTTONS | 16 /* A */; var GAMEPAD_DPAD_AB_BUTTONS = GAMEPAD_DPAD_A_BUTTONS | 32 /* B */; var GAMEPAD_DPAD_XY_BUTTONS = GAMEPAD_DPAD_BUTTONS | 1024 /* X */ | 2048 /* Y */; var GAMEPAD_GAMEPAD_EXTRA_BUTTONS = 32 /* B */ | 128 /* Select */ | 64 /* Menu */ | 256 /* Reset */; var standardGamepadMapping = [ [1 /* Left */, 14], [4 /* Right */, 15], [2 /* Up */, 12], [8 /* Down */, 13], [16 /* A */, 0], [32 /* B */, 1], [1024 /* X */, 2], [2048 /* Y */, 3], [128 /* Select */, 8], [64 /* Menu */, 9] ]; var GamepadServer = class extends SensorServer { constructor(options) { super(SRV_GAMEPAD, { instanceName: options?.instanceName, readingValues: [0, 0, 0], streamingInterval: 50 }); const { variant = 1 /* Thumb */, buttonsAvailable = 0 } = options || {}; this.variant = this.addRegister(263 /* Variant */, [ variant ]); this.buttonsAvailable = this.addRegister( 384 /* ButtonsAvailable */, [buttonsAvailable] ); } get isAnalog() { const [value] = this.buttonsAvailable.values(); return !(value & GAMEPAD_DPAD_BUTTONS); } async down(buttons) { const [currentButtons, x, y] = this.reading.values(); const newButtons = currentButtons | buttons; await this.updateReading(newButtons, x, y); } async up(buttons) { const [currentButtons, x, y] = this.reading.values(); const newButtons = currentButtons & ~buttons; await this.updateReading(newButtons, x, y); } async updateDirection(x, y) { const [button] = this.reading.values(); await this.updateReading(button, x, y); } /** * Read the state of a browser gamepad and apply it to the sensor * @param gamepad */ async update(gamepad) { const { buttons, axes } = gamepad; const [buttonsAvailable] = this.buttonsAvailable.values(); let newButtons = 0; for (const [b, id] of standardGamepadMapping) { if ((b & buttonsAvailable) == b && !!buttons[id].pressed) { newButtons |= b; } } let newX = 0, newY = 0; if (this.isAnalog) { const [axeLeftRight, axeUpDown] = axes; newX = axeLeftRight; newY = axeUpDown; } await this.updateReading(newButtons, newX, newY); } async updateReading(buttons, x, y) { const [oldButtons] = this.reading.values(); let newButtons = buttons; if (!this.isAnalog) { x = buttons & 1 /* Left */ ? -1 : buttons & 4 /* Right */ ? 1 : 0; y = buttons & 2 /* Up */ ? -1 : buttons & 8 /* Down */ ? 1 : 0; } else { const threshold = 0.4; const mask = ~GAMEPAD_DPAD_BUTTONS; newButtons = buttons & mask; if (x < -threshold) newButtons |= 1 /* Left */; else if (x > threshold) newButtons |= 4 /* Right */; if (y < -threshold) newButtons |= 2 /* Up */; else if (y > threshold) newButtons |= 8 /* Down */; } this.reading.setValues([newButtons, x, y]); if (newButtons !== oldButtons) { await this.sendEvent( 3 /* ButtonsChanged */, jdpack("u32", [newButtons]) ); } } }; // src/servers/dotmatrixserver.ts var DotMatrixServer = class extends JDServiceServer { constructor(columns, rows, options) { super(SRV_DOT_MATRIX); const { brightness, variant } = options || {}; this.dots = this.addRegister(2 /* Dots */, [new Uint8Array(0)]); this.rows = this.addRegister(385 /* Rows */, [rows]); this.columns = this.addRegister(386 /* Columns */, [columns]); if (brightness !== void 0) this.brightness = this.addRegister(1 /* Brightness */, [128]); if (variant !== void 0) this.variant = this.addRegister(263 /* Variant */, [variant]); this.rows.skipBoundaryCheck = true; this.rows.skipErrorInjection = true; if (variant === 1 /* LED */) this.addRegister(258 /* StreamingPreferredInterval */, [ 50 ]); this.rows.on(CHANGE, this.updateDotsBuffer.bind(this)); this.columns.on(CHANGE, this.updateDotsBuffer.bind(this)); this.updateDotsBuffer(); } updateDotsBuffer() { const [rows] = this.rows.values(); const [columns] = this.columns.values(); const n = columns * (rows + 7 >> 3); if (this.dots.data?.length !== n) { this.dots.data = new Uint8Array(n); this.dots.emit(CHANGE); } } }; // src/jdom/lightconstants.ts var LIGHT_PROG_SET_ALL = 208; var LIGHT_PROG_FADE = 209; var LIGHT_PROG_FADE_HSV = 210; var LIGHT_PROG_ROTATE_FWD = 211; var LIGHT_PROG_ROTATE_BACK = 212; var LIGHT_PROG_SHOW = 213; var LIGHT_PROG_RANGE = 214; var LIGHT_PROG_MODE = 215; var LIGHT_PROG_MODE1 = 216; var LIGHT_MODE_REPLACE = 0; var LIGHT_MODE_ADD_RGB = 1; var LIGHT_MODE_SUBTRACT_RGB = 2; var LIGHT_MODE_MULTIPLY_RGB = 3; var LIGHT_MODE_LAST = 3; var LIGHT_PROG_COLN = 192; var LIGHT_PROG_COL1 = 193; var LIGHT_PROG_COL2 = 194; var LIGHT_PROG_COL3 = 195; var LIGHT_PROG_COL1_SET = 207; // src/servers/ledstripserver.ts var PROG_EOF = 0; var PROG_CMD = 1; var PROG_NUMBER = 3; var PROG_COLOR_BLOCK = 4; function rgb(r, g, b) { return { r, g, b }; } function hsv(hue, sat, val) { hue = hue * 192 >> 8; const invsat = 255 - sat; const brightness_floor = val * invsat >> 8; const color_amplitude = val - brightness_floor; const section = hue / 64 >> 0; const offset = hue % 64 >> 0; const rampup = offset; const rampdown = 64 - 1 - offset; const rampup_amp_adj = rampup * color_amplitude / (256 / 4) >> 0; const rampdown_amp_adj = rampdown * color_amplitude / (256 / 4) >> 0; const rampup_adj_with_floor = rampup_amp_adj + brightness_floor; const rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor; let r = 0, g = 0, b = 0; if (section) { if (section == 1) { r = brightness_floor; g = rampdown_adj_with_floor; b = rampup_adj_with_floor; } else { r = rampup_adj_with_floor; g = brightness_floor; b = rampdown_adj_with_floor; } } else { r = rampdown_adj_with_floor; g = rampup_adj_with_floor; b = brightness_floor; } return rgb(r, g, b); } function mulcol(c, m) { let c2 = c * m >> 7; if (m < 128 && c == c2) c2--; else if (m > 128 && c == c2) c2++; return c2; } function clamp(c) { if (c < 0) return 0; if (c > 255) return 255; return c; } function SCALE0(c, i) { return (c & 255) * (1 + (i & 255)) >> 8; } var LedStripServer = class extends JDServiceServer { constructor(options) { super(SRV_LED_STRIP, options); this.pxbuffer = new Uint8Array(0); this.prog_mode = 0; this.prog_tmpmode = 0; this.range_start = 0; this.range_end = 0; this.range_len = 0; this.range_ptr = 0; this.prog_ptr = 0; this.prog_size = 0; this.prog_data = new Uint8Array(0); this.dirty = true; this.inited = false; this.power_enable = false; const { numColumns, maxPower = 200, maxPixels = 300, numPixels = 15 } = options || {}; this.brightness = this.addRegister(1 /* Brightness */, [ 15 ]); this.actualBrightness = this.addRegister( 384 /* ActualBrightness */, [15] ); this.lightType = this.addRegister( 128 /* LightType */, [0 /* WS2812B_GRB */] ); this.numPixels = this.addRegister(129 /* NumPixels */, [ numPixels ]); this.maxPower = this.addRegister(7 /* MaxPower */, [ maxPower ]); this.maxPixels = this.addRegister(385 /* MaxPixels */, [ maxPixels ]); this.numRepeats = this.addRegister(130 /* NumRepeats */, [ 0 ]); if (numColumns !== void 0) this.numColumns = this.addRegister( 131 /* NumColumns */, [numColumns] ); this.brightness.on( CHANGE, () => this.intensity = this.requested_intensity ); this.numPixels.on(CHANGE, this.allocRxBuffer.bind(this)); this.maxPixels.on(CHANGE, this.allocRxBuffer.bind(this)); this.addCommand(129 /* Run */, this.handleRun.bind(this)); this.allocRxBuffer(); } /** * Gets an array of RGB color numbers */ get colors() { return this.pxbuffer; } get maxpower() { const [r] = this.maxPower.values() || [200]; return r; } get maxpixels() { const [r] = this.maxPixels.values() || []; return r; } get numpixels() { const [r] = this.numPixels.values() || [0]; return r; } get requested_intensity() { const [r] = this.brightness.values() || [0]; return r; } get intensity() { const [r] = this.actualBrightness.values() || [0]; return r; } set intensity(v) { this.actualBrightness.setValues([v]); } jd_power_enable(value) { this.power_enable = value; } is_enabled() { return this.numpixels > 0 && this.requested_intensity > 0; } allocRxBuffer() { const { numpixels = 0, maxpixels, pxbuffer } = this; if (maxpixels !== void 0 && numpixels > maxpixels) this.numPixels.setValues([this.maxpixels]); const n = numpixels * 3; if (pxbuffer || n !== pxbuffer.length) this.pxbuffer = new Uint8Array(n); } reset_range() { this.range_ptr = this.range_start; } set_next(c) { if (this.range_ptr >= this.range_end) return false; const p = this.pxbuffer; const pi = this.range_ptr++ * 3; if (this.prog_tmpmode == LIGHT_MODE_REPLACE) { p[pi + 0] = c.r; p[pi + 1] = c.g; p[pi + 2] = c.b; return true; } let r = p[pi + 0], g = p[pi + 1], b = p[pi + 2]; switch (this.prog_tmpmode) { case LIGHT_MODE_ADD_RGB: r += c.r; g += c.g; b += c.b; break; case LIGHT_MODE_SUBTRACT_RGB: r -= c.r; g -= c.g; b -= c.b; break; case LIGHT_MODE_MULTIPLY_RGB: r = mulcol(r, c.r); g = mulcol(g, c.g); b = mulcol(b, c.b); break; } p[pi + 0] = clamp(r); p[pi + 1] = clamp(g); p[pi + 2] = clamp(b); return true; } limit_intensity() { const { numpixels, requested_intensity, maxpower, pxbuffer } = this; let n = numpixels * 3; const prev_intensity = this.intensity; let intensity = this.intensity; intensity += 1 + (intensity >> 5); if (requested_intensity !== void 0 && intensity > requested_intensity) intensity = requested_intensity; let current_full = 0; let current = 0; let current_prev = 0; let di = 0; while (n--) { const v = pxbuffer[di++]; current += SCALE0(v, intensity); current_prev += SCALE0(v, prev_intensity); current_full += v; } current *= 46; current_prev *= 46; current_full *= 46; const base_current = 14e3 + 930 * numpixels; const current_limit = maxpower * 1e3 - base_current; if (current <= current_limit) { this.intensity = intensity; return; } if (current_prev <= current_limit) { return; } let inten = current_limit / (current_full >> 8) - 1; if (inten < 0) inten = 0; this.intensity = inten; } prog_fetch_color() { const ptr = this.prog_ptr; if (ptr + 3 > this.prog_size) return rgb(0, 0, 0); const d = this.prog_data; this.prog_ptr = ptr + 3; return rgb(d[ptr + 0], d[ptr + 1], d[ptr + 2]); } prog_fetch() { if (this.prog_ptr >= this.prog_size) return { prog: PROG_EOF }; const d = this.prog_data; const c = d[this.prog_ptr++]; if (!(c & 128)) { return { dst: c, prog: PROG_NUMBER }; } else if ((c & 192) == 128) { return { dst: (c & 63) << 8 | d[this.prog_ptr++], prog: PROG_NUMBER }; } else switch (c) { case LIGHT_PROG_COL1: return { dst: 1, prog: PROG_COLOR_BLOCK }; case LIGHT_PROG_COL2: return { dst: 2, prog: PROG_COLOR_BLOCK }; case LIGHT_PROG_COL3: return { dst: 3, prog: PROG_COLOR_BLOCK }; case LIGHT_PROG_COLN: return { dst: d[this.prog_ptr++], prog: PROG_COLOR_BLOCK }; default: return { dst: c, prog: PROG_CMD }; } } prog_fetch_num(defl) { const prev = this.prog_ptr; const fr = this.prog_fetch(); const { dst: res, prog: r } = fr; if (r == PROG_NUMBER) return res; else { this.prog_ptr = prev; return defl; } } prog_fetch_cmd() { let cmd; for (; ; ) { const c = this.prog_fetch(); switch (c.prog) { case PROG_CMD: return c.dst; case PROG_COLOR_BLOCK: while (cmd--) this.prog_fetch_color(); break; case PROG_EOF: return 0; } } } prog_set(len) { this.reset_range(); const start = this.prog_ptr; for (; ; ) { this.prog_ptr = start; let ok = false; for (let i = 0; i < len; ++i) { ok = this.set_next(this.prog_fetch_color()); } if (!ok) break; } } prog_fade(len, usehsv) { if (len < 2) { this.prog_set(len); return; } let colidx = 0; const endp = this.prog_ptr + 3 * len; let col0 = this.prog_fetch_color(); let col1 = this.prog_fetch_color(); const colstep = (len - 1 << 16) / this.range_len; let colpos = 0; this.reset_range(); for (; ; ) { while (colidx < colpos >> 16) { colidx++; col0 = col1; col1 = this.prog_fetch_color(); } const fade1 = colpos & 65535; const fade0 = 65535 - fade1; const col = rgb( col0.r * fade0 + col1.r * fade1 + 32768 >> 16, col0.g * fade0 + col1.g * fade1 + 32768 >> 16, col0.b * fade0 + col1.b * fade1 + 32768 >> 16 ); if (!this.set_next(usehsv ? hsv(col.r, col.g, col.b) : col)) break; colpos += colstep; } this.prog_ptr = endp; } prog_rot(shift) { if (shift <= 0 || shift >= this.range_len) return; const range_start = this.range_start; const range_end = this.range_end; const buf = this.pxbuffer; let first = range_start * 3; let middle = (range_start + shift) * 3; const last = range_end * 3; let next = middle; while (first != next) { const tmp = buf[first]; const tmp1 = buf[first + 1]; const tmp2 = buf[first + 2]; buf[first] = buf[next]; buf[first + 1] = buf[next + 1]; buf[first + 2] = buf[next + 2]; buf[next] = tmp; buf[next + 1] = tmp1; buf[next + 2] = tmp2; first += 3; next += 3; if (next === last) next = middle; else if (first === middle) middle = next; } } fetch_mode() { const m = this.prog_fetch_num(0); if (m > LIGHT_MODE_LAST) return 0; return m; } prog_process() { const data = this.prog_data; if (this.prog_ptr >= this.prog_size) return false; while (data === this.prog_data) { const cmd = this.prog_fetch_cmd(); if (!cmd) break; if (cmd == LIGHT_PROG_SHOW) { const k = this.prog_fetch_num(50); this.dirty = true; setTimeout(this.animationFrame.bind(this), k); return data === this.prog_data; } switch (cmd) { case LIGHT_PROG_COL1_SET: this.range_ptr = this.range_start + this.prog_fetch_num(0); this.set_next(this.prog_fetch_color()); break; case LIGHT_PROG_FADE: case LIGHT_PROG_FADE_HSV: case LIGHT_PROG_SET_ALL: { const { dst: len, prog: pcmd } = this.prog_fetch(); if (pcmd != PROG_COLOR_BLOCK || len == 0) continue; if (cmd == LIGHT_PROG_SET_ALL) this.prog_set(len); else this.prog_fade(len, cmd == LIGHT_PROG_FADE_HSV); break; } case LIGHT_PROG_ROTATE_BACK: case LIGHT_PROG_ROTATE_FWD: { let k = this.prog_fetch_num(1); const len = this.range_len; if (len == 0) continue; while (k >= len) k -= len; if (cmd == LIGHT_PROG_ROTATE_FWD && k != 0) k = len - k; this.prog_rot(k); break; } case LIGHT_PROG_MODE1: this.prog_tmpmode = this.fetch_mode(); break; case LIGHT_PROG_MODE: this.prog_mode = this.fetch_mode(); break; case LIGHT_PROG_RANGE: { let start = this.prog_fetch_num(0); const len = this.prog_fetch_num(this.numpixels); const numpixels = this.numpixels; if (start > numpixels) start = numpixels; let end = start + len; if (end > numpixels) end = numpixels; this.range_start = start; this.range_end = end; this.range_len = end - start; break; } } if (cmd != LIGHT_PROG_MODE1) this.prog_tmpmode = this.prog_mode; } return false; } /** * Perform an animation step */ animationFrame() { if (!this.prog_process()) return; if (!this.is_enabled()) return; if (this.dirty) { this.dirty = false; if (isBufferEmpty(this.pxbuffer)) { this.jd_power_enable(false); this.emit(RENDER); return; } else { this.jd_power_enable(true); } this.limit_intensity(); this.emit(RENDER); } } sync_config() { if (!this.is_enabled()) { this.jd_power_enable(false); return; } if (!this.inited) { this.inited = true; } this.jd_power_enable(true); } handleRun(pkt) { this.prog_data = pkt.data; this.prog_size = this.prog_data.length; this.prog_ptr = 0; this.range_start = 0; this.range_end = this.range_len = this.numpixels; this.prog_tmpmode = this.prog_mode = 0; this.sync_config(); this.animationFrame(); } }; // src/servers/matrixkeypadserver.ts var MatrixKeypadServer = class extends SensorServer { constructor(columns, rows, labels) { super(SRV_MATRIX_KEYPAD, { readingValues: [[]] }); this.columns = this.addRegister(385 /* Columns */, [columns]); this.rows = this.addRegister(384 /* Rows */, [rows]); this.labels = this.addRegister( 386 /* Labels */, labels ? [labels.map((l) => [l])] : void 0 ); } async down(button) { const [values2] = this.reading.values(); const valuei = values2.findIndex((v) => v[0] === button); if (valuei < 0) { values2.push([button]); this.reading.setValues([values2]); } } async up(button) { const [values2] = this.reading.values(); const valuei = values2.findIndex((v) => v[0] === button); if (valuei > -1) { values2.splice(valuei, 1); this.reading.setValues([values2]); } } }; // src/servers/motorserver.ts var MotorServer = class extends JDServiceServer { constructor(instanceName, options) { super(SRV_MOTOR, { instanceName }); const { loadTorque, loadRotationSpeed } = options || {}; this.speed = this.addRegister(2 /* Speed */, [0]); this.enabled = this.addRegister(1 /* Enabled */, [false]); if (loadTorque) this.loadTorque = this.addRegister(384 /* LoadTorque */, [ loadTorque ]); if (loadRotationSpeed) this.loadRotationSpeed = this.addRegister( 385 /* LoadRotationSpeed */, [loadRotationSpeed] ); } }; // src/servers/leveldetector.ts var LevelDetector = class extends JDClient { constructor(service) { super(); this.service = service; this.reset(); if (this.service.inactiveThreshold) this.mount( this.service.inactiveThreshold.subscribe( CHANGE, this.reset.bind(this) ) ); if (this.service.activeThreshold) this.mount( this.service.activeThreshold.subscribe( CHANGE, this.reset.bind(this) ) ); this.mount( this.service.reading.subscribe(CHANGE, this.update.bind(this)) ); } reset() { this._state = 1 /* Neutral */; } update() { const [level] = this.service.reading.values(); if (level === void 0) { this.setState(1 /* Neutral */); return; } const [active] = this.service.activeThreshold?.values() || []; if (active !== void 0 && level >= active) { this.setState(3 /* Active */); return; } const [inactive] = this.service.inactiveThreshold?.values() || []; if (inactive !== void 0 && level <= inactive) { this.setState(2 /* Inactive */); return; } this.setState(1 /* Neutral */); } setState(state) { if (state === this._state) return; this._state = state; switch (state) { case 3 /* Active */: this.service.sendEvent(1 /* Active */); break; case 2 /* Inactive */: this.service.sendEvent(2 /* Inactive */); break; case 1 /* Neutral */: this.service.sendEvent(7 /* Neutral */); break; } } }; // src/servers/analogsensorserver.ts var AnalogSensorServer = class extends SensorServer { constructor(serviceClass2, options) { super(serviceClass2, options); const { minReading, maxReading, inactiveThreshold, activeThreshold, readingResolution } = options || {}; if (minReading !== void 0) this.addRegister(260 /* MinReading */, [minReading]); if (maxReading !== void 0) this.addRegister(261 /* MaxReading */, [maxReading]); if (readingResolution !== void 0) this.addRegister(264 /* ReadingResolution */, [ readingResolution ]); if (inactiveThreshold !== void 0 || this.activeThreshold !== void 0) { if (inactiveThreshold !== void 0) this.inactiveThreshold = this.addRegister( 5 /* InactiveThreshold */, [inactiveThreshold] ); if (activeThreshold !== void 0) this.activeThreshold = this.addRegister( 6 /* ActiveThreshold */, [activeThreshold] ); this.levelDetector = new LevelDetector(this); } } }; // src/servers/raingaugeserver.ts var RainGaugeServer = class extends AnalogSensorServer { constructor(options) { super(SRV_RAIN_GAUGE, { readingValues: [0] }); this._tiltCount = 0; this._level = 0; const { bucketSize } = options || {}; this.precipitationPrecision = this.addRegister( 264 /* PrecipitationPrecision */, [bucketSize || 0.2794] ); this._level = 0; } get tiltCount() { return this._tiltCount; } get level() { return this._level; } async rain(fraction) { if (!fraction) return; this._level += fraction; if (this._level >= 0.7) await this.tilt(); else this.emit(CHANGE); } async tilt() { this._tiltCount++; this._level = 0; const [bucket] = this.precipitationPrecision.values(); const [current] = this.reading.values(); this.reading.setValues([current + (bucket || 0.2)]); this.emit(CHANGE); } }; // src/servers/reflectedlightserver.ts var ReflectedLightServer = class extends AnalogSensorServer { constructor(options) { super(SRV_REFLECTED_LIGHT, { readingValues: [0] }); const { variant } = options || {}; this.variant = this.addRegister( 263 /* Variant */, [variant || 1 /* InfraredDigital */] ); } }; // src/servers/rotaryencoderserver.ts var RotaryEncoderServer = class extends SensorServer { constructor() { super(SRV_ROTARY_ENCODER, { readingValues: [0], streamingInterval: 50 }); this.clicksPerTurn = this.addRegister( 384 /* ClicksPerTurn */, [12] ); } async rotate(clicks) { const [position] = this.reading.values(); this.reading.setValues([position + (clicks >> 0)]); } }; // src/servers/servoserver.ts var ServoServer = class extends JDServiceServer { constructor(options) { super(SRV_SERVO, options); const { minAngle = 0, maxAngle = 180, responseSpeed, stallTorque } = options || {}; this.angle = this.addRegister(2 /* Angle */, [0]); this.enabled = this.addRegister(1 /* Enabled */, [false]); this.minAngle = this.addRegister(272 /* MinAngle */, [ minAngle ]); this.maxAngle = this.addRegister(273 /* MaxAngle */, [ maxAngle ]); this.offset = this.addRegister(129 /* Offset */, [0]); this.responseSpeed = this.addRegister( 385 /* ResponseSpeed */, responseSpeed !== void 0 ? [responseSpeed] : void 0 ); this.stallTorque = this.addRegister( 384 /* StallTorque */, stallTorque !== void 0 ? [stallTorque] : void 0 ); this.angle.on(PACKET_DATA_NORMALIZE, (values2) => { let angle = values2[0]; const [minAngle2] = this.minAngle.values(); const [maxAngle2] = this.maxAngle.values(); if (minAngle2 !== void 0) angle = Math.max(minAngle2, angle); if (maxAngle2 !== void 0) angle = Math.min(maxAngle2, angle); values2[0] = angle; }); } }; // src/servers/settingsserver.ts var SettingsServer = class extends JDServiceServer { constructor(store) { super(SRV_SETTINGS); this.store = store; this.addCommand(128 /* Get */, this.handleGet.bind(this)); this.addCommand(129 /* Set */, this.handleSet.bind(this)); this.addCommand(132 /* Delete */, this.handleDelete.bind(this)); this.addCommand(130 /* ListKeys */, this.handleListKeys.bind(this)); this.addCommand(131 /* List */, this.handleList.bind(this)); this.addCommand(133 /* Clear */, this.handleClear.bind(this)); this.settings = this.read(); } getPayload(key) { const value = this.settings[key]; const secret = /^$/.test(key); let payload; if (value === void 0) { payload = new Uint8Array(0); } else if (secret) { const payload2 = new Uint8Array(1); payload2[0] = 0; } else { payload = fromHex(value); } return payload; } async handleGet(pkt) { const [key] = pkt.jdunpack("s"); const payload = this.getPayload(key); const resp = Packet.jdpacked( 128 /* Get */, "z b", [key, payload] ); await this.sendPacketAsync(resp); } async handleSet(pkt) { const [key, value] = pkt.jdunpack("z b"); this.settings[key] = toHex2(value); await this.save(); } async handleDelete(pkt) { const [key] = pkt.jdunpack("s"); delete this.settings[key]; await this.save(); } async handleListKeys(pkt) { const pipe = OutPipe.from(this.device.bus, pkt, true); await pipe.respondForEach( Object.keys(this.settings), (k) => jdpack("s", [k]) ); } async handleList(pkt) { const pipe = OutPipe.from(this.device.bus, pkt, true); await pipe.respondForEach(Object.keys(this.settings), (k) => { const payload = this.getPayload(k); return jdpack("z b", [k, payload]); }); } handleClear() { this.settings = {}; this.save(); } read() { if (!this.store) return {}; try { return JSON.parse(this.store.get() || "{}"); } catch (e) { console.debug(e); return {}; } } async save() { this.store?.set(JSON.stringify(this.settings)); await this.sendEvent(3 /* Change */); } }; // src/servers/speechsynthesisserver.ts var SpeechSynthesisServer = class extends JDServiceServer { constructor() { super(SRV_SPEECH_SYNTHESIS); this.synthesis = typeof self !== "undefined" && window.speechSynthesis; this.enabled = this.addRegister(1 /* Enabled */, [ !this.synthesis?.paused ]); this.pitch = this.addRegister(130 /* Pitch */, [1]); this.rate = this.addRegister(131 /* Rate */, [1]); this.lang = this.addRegister(128 /* Lang */, [""]); this.volume = this.addRegister(129 /* Volume */, [ 0.5 ]); this.addCommand(128 /* Speak */, this.handleSpeak.bind(this)); this.addCommand(129 /* Cancel */, this.handleCancel.bind(this)); } handleSpeak(pkt) { const [text] = pkt.jdunpack("s"); if (!this.synthesis || !text) return; const [pitch] = this.pitch.values(); const [rate] = this.pitch.values(); const [lang] = this.lang.values(); const [volume] = this.volume.values(); const utterance = new SpeechSynthesisUtterance(text); utterance.pitch = pitch; utterance.rate = rate; utterance.lang = lang; utterance.volume = volume; this.synthesis.speak(utterance); } handleCancel(pkt) { this.synthesis?.cancel(); } }; // src/servers/switchserver.ts var SwitchServer = class extends SensorServer { constructor(options) { super(SRV_SWITCH, { readingValues: [false], streamingInterval: 50 }); const { variant } = options || {}; this.variant = this.addRegister( 263 /* Variant */, variant !== void 0 ? [variant] : void 0 ); } async toggle() { const [v] = this.reading.values(); if (!v) await this.switchOn(); else await this.switchOff(); } async switchOn() { const [v] = this.reading.values(); if (!v) { this.reading.setValues([true]); await this.sendEvent(1 /* On */); } } async switchOff() { const [v] = this.reading.values(); if (v) { this.reading.setValues([false]); await this.sendEvent(2 /* Off */); } } }; // src/servers/trafficlightserver.ts var TrafficLightServer = class extends JDServiceServer { constructor(options) { super(SRV_TRAFFIC_LIGHT, options); this.red = this.addRegister(128 /* Red */, [false]); this.yellow = this.addRegister(129 /* Yellow */, [false]); this.green = this.addRegister(130 /* Green */, [false]); } }; // src/servers/soundplayerserver.ts var SoundPlayerServer = class extends JDServiceServer { constructor(sounds) { super(SRV_SOUND_PLAYER); this.sounds = sounds; this.volume = this.addRegister(1 /* Volume */, [0.5]); this.addCommand( 130 /* ListSounds */, this.handleListSounds.bind(this) ); this.addCommand(128 /* Play */, this.handlePlay.bind(this)); } async handleListSounds(pkt) { const pipe = OutPipe.from(this.device.bus, pkt, true); await pipe.respondForEach( this.sounds, (sound) => jdpack("u32 s", sound) ); } handlePlay(pkt) { const [name] = pkt.jdunpack("s"); this.onPlay?.(name); } }; // src/servers/randomnumbergeneratorserver.ts var RandomNumberGeneratorServer = class extends JDServiceServer { constructor() { super(SRV_RNG, { variant: 3 /* WebCrypto */ }); this.reading = this.addRegister(384 /* Random */, [new Uint8Array(64)]); this.reading.on(REGISTER_PRE_GET, this.handleRefresh.bind(this)); } handleRefresh() { const data = new Uint8Array(64); if (typeof window !== "undefined") window.crypto.getRandomValues(data); this.reading.setValues([data], true); } }; // src/servers/compassserver.ts var CompassServer = class extends AnalogSensorServer { constructor() { super(SRV_COMPASS, { readingValues: [0], minReading: 0, maxReading: 360, readingError: [1] }); this.enabled = this.addRegister(1 /* Enabled */, [false]); this.enabled.on(CHANGE, () => { const [status] = this.statusCode.values(); if (status === 100 /* CalibrationNeeded */) { console.debug("start calibration"); this.calibrate(); } }); } }; // src/servers/dmxserver.ts var DMXServer = class extends JDServiceServer { constructor() { super(SRV_DMX, { intensityValues: [0] }); this.addCommand(128 /* Send */, this.handleSend.bind(this)); } handleSend(pkt) { console.debug(`dmx send`, toHex2(pkt.data)); } }; // src/jdom/iframeclient.ts function inIFrame() { try { return typeof window !== "undefined" && window.self !== window.top; } catch (e) { return typeof window !== "undefined"; } } // src/servers/bitradioserver.ts var RADIO_MAX_PACKET_SIZE = 32; var MAX_PAYLOAD_LENGTH = 20; var PACKET_PREFIX_LENGTH = 9; var VALUE_PACKET_NAME_LEN_OFFSET = 13; var DOUBLE_VALUE_PACKET_NAME_LEN_OFFSET = 17; var PACKET_TYPE_NUMBER = 0; var PACKET_TYPE_VALUE = 1; var PACKET_TYPE_STRING = 2; var PACKET_TYPE_BUFFER = 3; var PACKET_TYPE_DOUBLE = 4; var PACKET_TYPE_DOUBLE_VALUE = 5; function getStringOffset(packetType) { switch (packetType) { case PACKET_TYPE_STRING: return PACKET_PREFIX_LENGTH; case PACKET_TYPE_VALUE: return VALUE_PACKET_NAME_LEN_OFFSET; case PACKET_TYPE_DOUBLE_VALUE: return DOUBLE_VALUE_PACKET_NAME_LEN_OFFSET; default: return void 0; } } function truncateString(str) { return str; } var RadioPacket = class { constructor(data) { this.data = data; if (!data) this.data = new Uint8Array(RADIO_MAX_PACKET_SIZE + 4); } static getPacket(data) { if (!data) return void 0; return new RadioPacket(data); } static mkPacket(packetType) { const res = new RadioPacket(); res.data[0] = packetType; return res; } get signal() { return getNumber(this.data, 5 /* Int32LE */, this.data.length - 4); } get packetType() { return this.data[0]; } get time() { return getNumber(this.data, 5 /* Int32LE */, 1); } set time(val) { setNumber(this.data, 5 /* Int32LE */, 1, val); } get serial() { return getNumber(this.data, 5 /* Int32LE */, 5); } set serial(val) { setNumber(this.data, 5 /* Int32LE */, 5, val); } get stringPayload() { const offset = getStringOffset(this.packetType); return offset ? this.data.slice(offset + 1, this.data[offset]).toString() : void 0; } set stringPayload(val) { const offset = getStringOffset(this.packetType); if (offset) { const buf = stringToBuffer(truncateString(val)); this.data[offset] = buf.length; memcpy(this.data, offset + 1, buf); } } get numberPayload() { switch (this.packetType) { case PACKET_TYPE_NUMBER: case PACKET_TYPE_VALUE: return getNumber( this.data, 5 /* Int32LE */, PACKET_PREFIX_LENGTH ); case PACKET_TYPE_DOUBLE: case PACKET_TYPE_DOUBLE_VALUE: return getNumber( this.data, 14 /* Float64LE */, PACKET_PREFIX_LENGTH ); } return void 0; } set numberPayload(val) { switch (this.packetType) { case PACKET_TYPE_NUMBER: case PACKET_TYPE_VALUE: setNumber( this.data, 5 /* Int32LE */, PACKET_PREFIX_LENGTH, val ); break; case PACKET_TYPE_DOUBLE: case PACKET_TYPE_DOUBLE_VALUE: setNumber( this.data, 14 /* Float64LE */, PACKET_PREFIX_LENGTH, val ); break; } } get bufferPayload() { const len = this.data[PACKET_PREFIX_LENGTH]; return this.data.slice( PACKET_PREFIX_LENGTH + 1, PACKET_PREFIX_LENGTH + 1 + len ); } set bufferPayload(b) { const len = Math.min(b.length, MAX_PAYLOAD_LENGTH - 1); this.data[PACKET_PREFIX_LENGTH] = len; memcpy(this.data, PACKET_PREFIX_LENGTH + 1, b, 0, len); } hasString() { return this.packetType === PACKET_TYPE_STRING || this.packetType === PACKET_TYPE_VALUE || this.packetType === PACKET_TYPE_DOUBLE_VALUE; } hasNumber() { return this.packetType === PACKET_TYPE_NUMBER || this.packetType === PACKET_TYPE_DOUBLE || this.packetType === PACKET_TYPE_VALUE || this.packetType === PACKET_TYPE_DOUBLE_VALUE; } }; var BitRadioServer = class extends JDServiceServer { constructor() { super(SRV_BIT_RADIO); this.enabled = this.addRegister(1 /* Enabled */, [false]); this.group = this.addRegister(128 /* Group */, [1]); this.transmissionPower = this.addRegister( 129 /* TransmissionPower */, [6] ); this.frequencyBand = this.addRegister( 130 /* FrequencyBand */, [7] ); this.addCommand( 128 /* SendString */, this.handleSendString.bind(this) ); this.addCommand( 129 /* SendNumber */, this.handleSendNumber.bind(this) ); this.addCommand(130 /* SendValue */, this.handleSendValue.bind(this)); this.addCommand( 131 /* SendBuffer */, this.handleSendBuffer.bind(this) ); } handleSendString(pkt) { const [message] = pkt.jdunpack("s"); const rpkt = RadioPacket.mkPacket(PACKET_TYPE_STRING); rpkt.stringPayload = message; this.sendRadioPacket(rpkt); } handleSendNumber(pkt) { const [value] = pkt.jdunpack("f64"); const rpkt = RadioPacket.mkPacket(PACKET_TYPE_DOUBLE); rpkt.numberPayload = value; this.sendRadioPacket(rpkt); } handleSendValue(pkt) { const [value, name] = pkt.jdunpack("f64 s"); const rpkt = RadioPacket.mkPacket(PACKET_TYPE_DOUBLE_VALUE); rpkt.stringPayload = name; rpkt.numberPayload = value; this.sendRadioPacket(rpkt); } handleSendBuffer(pkt) { const { data } = pkt; const rpkt = RadioPacket.mkPacket(PACKET_TYPE_BUFFER); rpkt.bufferPayload = data; this.sendRadioPacket(rpkt); } sendRadioPacket(rpkt) { const [on] = this.enabled.values(); if (!on) return; const { bus } = this.device; const { timestamp: timestamp2 } = bus; const [group] = this.group.values(); rpkt.time = timestamp2; rpkt.serial = 0; const msg = { type: "radiopacket", broadcast: true, // TODO rssi: -75, // TODO serial: 0, time: bus.timestamp, payload: { groupId: group, type: 0, // buffer bufferData: rpkt.data } }; if (inIFrame()) { window.parent.postMessage(msg, "*"); } } }; // src/servers/powerserver.ts var PowerServer = class extends JDServiceServer { constructor(options) { super(SRV_POWER, options); this.allowed = this.addRegister(1 /* Allowed */, [false]); this.maxPower = this.addRegister(7 /* MaxPower */, [500]); this.powerStatus = this.addRegister( 385 /* PowerStatus */, [0 /* Disallowed */] ); this.currentDraw = this.addRegister(257 /* CurrentDraw */, [0]); this.keepOnPulseDuration = this.addRegister( 128 /* KeepOnPulseDuration */, [10] ); this.keepOnPulsePeriod = this.addRegister( 129 /* KeepOnPulsePeriod */, [1e3] ); this.allowed.on(CHANGE, this.handleAllowedChange.bind(this)); } handleAllowedChange() { const allowed = !!this.allowed.values()[0]; if (allowed) { this.powerStatus.setValues([1 /* Powering */]); this.currentDraw.setValues([250]); } else { this.powerStatus.setValues([0 /* Disallowed */]); this.currentDraw.setValues([0]); } } }; // src/servers/capacitivebuttonserver.ts var CapacitiveButtonServer = class extends JDServiceServer { constructor(options) { super(SRV_CAPACITIVE_BUTTON, options); const { threshold = 0.5 } = options || {}; this.threshold = this.addRegister(6 /* Threshold */, [ threshold ]); } }; // src/servers/hidkeyboardserver.ts var HIDKeyboardServer = class extends JDServiceServer { constructor(options) { super(SRV_HID_KEYBOARD, options); this.addCommand(129 /* Clear */, this.handleClear.bind(this)); this.addCommand(128 /* Key */, this.handleKey.bind(this)); } get lastKey() { return this._lastKey; } handleKey(pkt) { const [rest] = pkt.jdunpack(HidKeyboardCmdPack.Key); this._lastKey = rest; this.emit(CHANGE); } handleClear() { if (this._lastKey) { this._lastKey = void 0; this.emit(CHANGE); } } }; var selectors = { a: 4, b: 5, c: 6, d: 7, e: 8, f: 9, g: 10, h: 11, i: 12, j: 13, k: 14, l: 15, m: 16, n: 17, o: 18, p: 19, q: 20, r: 21, s: 22, t: 23, u: 24, v: 25, w: 26, x: 27, y: 28, z: 29, "1": 30, "2": 31, "3": 32, "4": 33, "5": 34, "6": 35, "7": 36, "8": 37, "9": 38, "0": 39, "!": 30, "@": 31, "#": 32, $: 33, "%": 34, "^": 35, "&": 36, "*": 37, "(": 38, ")": 39, enter: 40, escape: 41, backspace: 42, tab: 43, space: 44, " ": 44, "-": 45, _: 45, "=": 46, "+": 46, "[": 47, "{": 47, "]": 48, "}": 48, "\\": 49, "|": 49, // non-US # "~": 50, ";": 51, ":": 51, "'": 52, '"': 52, "`": 53, ",": 54, //"<": 0x37, ".": 55, //">": 0x37, "/": 56, "?": 56, capslock: 57, f1: 58, f2: 59, f3: 60, f4: 61, f5: 62, f6: 63, f7: 64, f8: 65, f9: 66, f10: 67, f11: 68, f12: 69, printscreen: 70, scrolllock: 71, pause: 72, insert: 73, home: 74, pageup: 75, delete: 76, end: 77, pagedown: 78, arrowright: 79, arrowleft: 80, arrowdown: 81, arrowup: 82, numlock: 83, numpaddivide: 84, numpadmultiply: 85, numpadsubstract: 86, numpadadd: 87, numpadenter: 88, numpad1: 89, numpad2: 90, numpad3: 91, numpad4: 92, numpad5: 93, numpad6: 94, numpad7: 95, numpad8: 96, numpad9: 97, numpad0: 98, numpaddecimal: 99, numpadequal: 103, f13: 104, f14: 105, f15: 106, f16: 107, f17: 108, f18: 109, f19: 110, f20: 111, f21: 112, f22: 113, f23: 114, f24: 115, execute: 116, help: 117, contextmenu: 118, select: 119, stop: 120, again: 121, undo: 122, cut: 123, copy: 124, paste: 125, find: 126, mute: 127, volumeup: 128, volumedown: 129, numpadcomma: 133 }; var reverseSelectors = Object.keys( selectors ).reduce( (r, key) => { if (!r[selectors[key]]) r[selectors[key]] = key; return r; }, {} ); var modifierCodes = { controlleft: 1 /* LeftControl */, altleft: 4 /* LeftAlt */, shiftleft: 2 /* LeftShift */, metaleft: 8 /* LeftGUI */, controlright: 16 /* RightControl */, altright: 64 /* RightAlt */, shiftright: 32 /* RightShift */, metaright: 128 /* RightGUI */ }; function renderKeyboardKey(selector, modifiers, pretty) { const flags = pretty ? [ "Ctrl", "Shift", "Alt", "Cmd", "Ctrl Right", "Shift Right", "AltRight", "Cmd Right" ] : [ "{controlleft}", "{shiftleft}", "{altleft}", "{metaleft}", "{controlright}", "{shiftright}", "{altright}", "{metaright}" ]; const sep = pretty ? " + " : " "; const values2 = []; flags.forEach((flag, i) => { if (modifiers & 1 << i) { values2.push(flag); } }); const sel = reverseSelectors[selector]; if (sel !== void 0) values2.push( pretty ? sel.toUpperCase() : !pretty && sel.length > 1 ? `{${sel}}` : sel ); const value = values2.filter((v) => !!v).join(sep); return value; } // src/servers/hidmouseserver.ts function renderHidMouseButtons(buttons) { const btns = [ buttons & 1 /* Left */ ? "left" : "", buttons & 2 /* Right */ ? "right" : "", buttons & 4 /* Middle */ ? "middle" : "" ].filter((b) => !!b).join(", "); return btns; } var HIDMouseServer = class extends JDServiceServer { constructor(options) { super(SRV_HID_MOUSE, options); this.addCommand(129 /* Move */, this.handleMove.bind(this)); this.addCommand(128 /* SetButton */, this.handleSetButton.bind(this)); this.addCommand(130 /* Wheel */, this.handleWheel.bind(this)); } get lastCommand() { return this._lastCommand; } setLastCommand(s) { if (this._lastCommand !== s) { this._lastCommand = s; this.emit(CHANGE); } } handleMove(pkt) { const [dx, dy, time] = pkt.jdunpack("i16 i16 u16"); this.setLastCommand(`move ${dx} ${dy} ${time}`); } handleSetButton(pkt) { const [buttons, event] = pkt.jdunpack("u16 u8"); const btns = renderHidMouseButtons(buttons); this.setLastCommand( `set buttons ${btns || "?"} ${(HidMouseButtonEvent[event] || "?").toLocaleLowerCase()}` ); } handleWheel(pkt) { const [dy, time] = pkt.jdunpack("i16 u16"); this.setLastCommand(`wheel ${dy} ${time}`); } }; // src/servers/vibrationmotorserver.ts var _VibrationMotorServer = class extends JDServiceServer { constructor(options) { super(SRV_VIBRATION_MOTOR, options); this._animationStep = -1; const { maxVibrations = 10 } = options || {}; this.maxVibrations = this.addRegister( 384 /* MaxVibrations */, [maxVibrations] ); this.addCommand( 128 /* Vibrate */, this.handleVibrate.bind(this) ); this.on(REFRESH, this.handleRefresh.bind(this)); } handleRefresh() { if (!this._animation) return; const { start, pattern } = this._animation; const now = this.device.bus.timestamp; const elapsed = now - start; let t = 0; for (let i = 0; i < pattern.length; ++i) { const [duration, speed] = pattern[i]; const dt = duration << 3; t += dt; if (t - dt <= elapsed && t > elapsed) { if (this._animationStep !== i) { this._animationStep = i; this.emit(_VibrationMotorServer.VIBRATE_PATTERN, { duration, speed }); } break; } } if (elapsed > t) { this._animation = void 0; this._animationStep = -1; this.emit(_VibrationMotorServer.VIBRATE_PATTERN, { duration: 0, speed: 0 }); this.emit(CHANGE); } } handleVibrate(pkt) { const [pattern] = pkt.jdunpack("r: u8 u0.8"); this._animation = { start: this.device.bus.timestamp, pattern }; this._animationStep = -1; if (pattern.length) { const [duration, speed] = pattern[0]; this._animationStep = 0; this.emit(_VibrationMotorServer.VIBRATE_PATTERN, { duration, speed }); } this.emit(CHANGE); } }; var VibrationMotorServer = _VibrationMotorServer; VibrationMotorServer.VIBRATE_PATTERN = "vibratePattern"; // src/servers/wifiserver.ts var WifiServer = class extends JDServiceServer { constructor(options) { super(SRV_WIFI, { intensityValues: [true] }); this._lastScanResults = options?.scanResults || []; this._knownNetworks = options?.knownNetworks || []; this.enabled = this.addRegister(1 /* Enabled */, [true]); this.ssid = this.addRegister(387 /* Ssid */, [""]); this.ipAddress = this.addRegister(385 /* IpAddress */, [ new Uint8Array(0) ]); this.eui48 = this.addRegister(386 /* Eui48 */, [ new Uint8Array(0) ]); this.addCommand(134 /* Scan */, this.handleScan.bind(this)); this.addCommand(130 /* Reconnect */, this.handleReconnect.bind(this)); this.addCommand( 128 /* LastScanResults */, this.handleLastScanResults.bind(this) ); this.addCommand( 135 /* ListKnownNetworks */, this.handleListKnownNetworks.bind(this) ); this.addCommand(129 /* AddNetwork */, this.handleAddNetwork.bind(this)); this.addCommand( 132 /* ForgetAllNetworks */, this.handleForgetAllNetworks.bind(this) ); this.addCommand( 131 /* ForgetNetwork */, this.handleForgetNetwork.bind(this) ); this.addCommand( 133 /* SetNetworkPriority */, this.handleSetNetworkPriority.bind(this) ); this.ipAddress.on(CHANGE, this.handleIpChange.bind(this)); this.enabled.on(CHANGE, this.handleEnabledChange.bind(this)); } handleEnabledChange() { const [enabled] = this.enabled.values(); if (!enabled) this.disconnect(); else this.connect(); } handleIpChange() { const [ip] = this.ipAddress.values(); if (ip?.length) this.sendEvent(1 /* GotIp */); else this.sendEvent(2 /* LostIp */); } get scannedKnownNetworks() { return this._lastScanResults.filter( (n) => this._knownNetworks.some((kn) => kn.ssid === n.ssid) ); } handleReconnect() { this.disconnect(); if (this.scannedKnownNetworks.length) { console.debug(`wifi: reconnect, connect`); this.enabled.setValues([true]); } } connect() { const network = this.scannedKnownNetworks[0]; const { ssid } = network || {}; this.ssid.setValues([ssid || ""]); this.enabled.setValues([!!ssid]); this.ipAddress.setValues([ssid ? randomBytes(4) : new Uint8Array(0)]); } disconnect() { this.ssid.setValues([""]); this.enabled.setValues([false]); this.ipAddress.setValues([new Uint8Array(0)]); } scan() { this.sendEvent( 128 /* ScanComplete */, jdpack("u16 u16", [ this._lastScanResults.length, this.scannedKnownNetworks.length ]) ); } handleScan() { this.scan(); } async handleLastScanResults(pkt) { const pipe = OutPipe.from(this.device.bus, pkt, true); await pipe.respondForEach( this._lastScanResults, ({ flags, rssi, channel, bssid, ssid }) => jdpack( "u32 x[4] i8 u8 b[6] s[33]", [flags, rssi, channel, bssid, ssid] ) ); } async handleListKnownNetworks(pkt) { const pipe = OutPipe.from(this.device.bus, pkt, true); await pipe.respondForEach( this._knownNetworks, ({ priority, flags, ssid }) => jdpack("i16 i16 s", [ priority, flags, ssid ]) ); } handleAddNetwork(pkt) { const [ssid, password] = pkt.jdunpack("z z"); let network = this._knownNetworks.find((n) => n.ssid === ssid); if (!network) { const scanned = this._lastScanResults.find((s) => s.ssid === ssid); this._knownNetworks.push( network = { ssid, flags: scanned?.flags, priority: 0, password: "" } ); } network.password = password; this.sendEvent(129 /* NetworksChanged */); } handleForgetAllNetworks() { this._knownNetworks = []; this.disconnect(); this.sendEvent(129 /* NetworksChanged */); } handleForgetNetwork(pkt) { const [ssid] = pkt.jdunpack("s"); this._knownNetworks = this._knownNetworks.filter( (network) => network.ssid !== ssid ); const [currentSsid] = this.ssid.values(); if (ssid === currentSsid) this.disconnect(); this.sendEvent(129 /* NetworksChanged */); } handleSetNetworkPriority(pkt) { const [priority, ssid] = pkt.jdunpack("i16 s"); const network = this._knownNetworks.find( (network2) => network2.ssid === ssid ); if (network) network.priority = priority; this.sendEvent(129 /* NetworksChanged */); } }; // src/servers/accelerometerserver.ts var AccelerometerServer = class extends SensorServer { constructor() { super(SRV_ACCELEROMETER, { readingValues: [0.5, 0.5, -(1 - (0.5 * 0.5 + 0.5 * 0.5))], preferredStreamingInterval: 20 }); this.maxForce = this.addRegister(8 /* MaxForce */, [ 2 ]); } }; // src/servers/brailledisplayserver.ts var BrailleDisplayServer = class extends JDServiceServer { constructor(options) { super(SRV_BRAILLE_DISPLAY, options); const { patterns = "", length = 12 } = options || {}; this.patterns = this.addRegister(2 /* Patterns */, [ patterns ]); this.enabled = this.addRegister(1 /* Enabled */, [ false ]); this.length = this.addRegister(385 /* Length */, [ length ]); } }; // src/servers/ledserver.ts function SCALE02(c, i) { return (c & 255) * (1 + (i & 255)) >> 8; } var LedServer = class extends JDServiceServer { constructor(options) { super(SRV_LED, options); const { numColumns, maxPower = 200, numPixels = 10, ledsPerPixel, luminousIntensity, waveLength } = options || {}; const n = Math.min(CONST_LED_MAX_PIXELS_LENGTH, numPixels); this.pixels = this.addRegister(2 /* Pixels */, [ new Uint8Array(n * 3) ]); this.brightness = this.addRegister(1 /* Brightness */, [15]); this.actualBrightness = this.addRegister( 384 /* ActualBrightness */, [15] ); this.numPixels = this.addRegister(386 /* NumPixels */, [n]); this.maxPower = this.addRegister(7 /* MaxPower */, [maxPower]); if (numColumns !== void 0) this.numColumns = this.addRegister(387 /* NumColumns */, [ numColumns ]); if (ledsPerPixel !== void 0) this.ledsPerPixel = this.addRegister(388 /* LedsPerPixel */, [ ledsPerPixel ]); if (luminousIntensity !== void 0) this.luminousIntensity = this.addRegister( 390 /* LuminousIntensity */, [luminousIntensity] ); if (waveLength !== void 0) this.waveLength = this.addRegister(389 /* WaveLength */, [waveLength]); this.brightness.on(CHANGE, () => { this.intensity = this.requested_intensity; this.limit_intensity(); }); this.pixels.on(CHANGE, () => this.limit_intensity()); this.on(FRAME_PROCESS_LARGE, this.handleFrameLarge.bind(this)); } handleFrameLarge(topic, data) { if (topic === "leds") this.pixels.setValues([data]); } /** * Gets an array of RGB color numbers */ get colors() { return this.pixels.data; } get maxpower() { const [r] = this.maxPower.values() || [200]; return r; } get numpixels() { const [r] = this.numPixels.values() || [0]; return r; } get requested_intensity() { const [r] = this.brightness.values() || [0]; return r; } get intensity() { const [r] = this.actualBrightness.values() || [0]; return r; } set intensity(v) { this.actualBrightness.setValues([v]); } get enabled() { return this.numpixels > 0 && this.requested_intensity > 0; } limit_intensity() { const pxbuffer = this.pixels.data; const { numpixels, requested_intensity, maxpower } = this; let n = numpixels * 3; const prev_intensity = this.intensity; let intensity = this.intensity; intensity += 1 + (intensity >> 5); if (requested_intensity !== void 0 && intensity > requested_intensity) intensity = requested_intensity; let current_full = 0; let current = 0; let current_prev = 0; let di = 0; while (n--) { const v = pxbuffer[di++]; current += SCALE02(v, intensity); current_prev += SCALE02(v, prev_intensity); current_full += v; } current *= 46; current_prev *= 46; current_full *= 46; const base_current = 14e3 + 930 * numpixels; const current_limit = maxpower * 1e3 - base_current; if (current <= current_limit) { this.intensity = intensity; return; } if (current_prev <= current_limit) { return; } let inten = current_limit / (current_full >> 8) - 1; if (inten < 0) inten = 0; this.intensity = inten; } }; // src/servers/powersupplyserver.ts var PowerSupplyServer = class extends JDServiceServer { constructor(options) { super(SRV_POWER_SUPPLY); const { outputVoltage, minVoltage, maxVoltage } = options || {}; this.enabled = this.addRegister(1 /* Enabled */, [false]); this.outputVoltage = this.addRegister(2 /* OutputVoltage */, [ outputVoltage ]); this.minVoltage = this.addRegister(272 /* MinimumVoltage */, [ minVoltage ]); this.maxVoltage = this.addRegister(273 /* MaximumVoltage */, [ maxVoltage ]); } }; // src/servers/hidjoystickserver.ts var HIDJoystickServer = class extends JDServiceServer { constructor(options) { super(SRV_HID_JOYSTICK, options); this.buttons = new Uint8Array(); this.axis = new Uint8Array(); const { buttonCount = 16, axisCount = 6, buttonsAnalog = false } = options || {}; this.addRegister(384 /* ButtonCount */, [buttonCount]); this.addRegister(386 /* AxisCount */, [axisCount]); this.addRegister(385 /* ButtonsAnalog */, [buttonsAnalog]); this.addCommand( 128 /* SetButtons */, this.handleSetButtons.bind(this) ); this.addCommand(129 /* SetAxis */, this.handleSetAxis.bind(this)); } handleSetButtons(pkt) { if (!bufferEq(this.buttons, pkt.data)) { this.buttons = pkt.data; this.emit(CHANGE); } } handleSetAxis(pkt) { if (!bufferEq(this.axis, pkt.data)) { this.axis = pkt.data; this.emit(CHANGE); } } }; // src/servers/magneticfieldlevelserver.ts var _MagneticFieldLevelServer = class extends SensorServer { constructor(options) { super(SRV_MAGNETIC_FIELD_LEVEL, { ...options, readingValues: [0] }); this._state = 1 /* Neutral */; this.reading.on(CHANGE, this.update.bind(this)); } active() { this.reading.setValues([1]); } inactive() { this.reading.setValues([0]); } get variant() { const reg = this.register(263 /* Variant */); const [v] = reg.values(); return v; } update() { const [strength] = this.reading.values(); if (Math.abs(strength) >= _MagneticFieldLevelServer.ACTIVE_THRESHOLD) { this.setState(3 /* Active */); } else if (Math.abs(strength) <= _MagneticFieldLevelServer.INACTIVE_THRESHOLD) { this.setState(2 /* Inactive */); } else this.setState(1 /* Neutral */); } setState(state) { if (state === this._state) return; const variant = this.variant; this._state = state; switch (state) { case 3 /* Active */: this.sendEvent(1 /* Active */); break; case 2 /* Inactive */: this.sendEvent(2 /* Inactive */); break; } } }; var MagneticFieldLevelServer = _MagneticFieldLevelServer; MagneticFieldLevelServer.ACTIVE_THRESHOLD = 0.3; MagneticFieldLevelServer.INACTIVE_THRESHOLD = 0.1; // src/servers/dualmotorsserver.ts var DualMotorsServer = class extends JDServiceServer { constructor(instanceName, options) { super(SRV_DUAL_MOTORS, { instanceName }); const { loadTorque, loadRotationSpeed } = options || {}; this.speed = this.addRegister( 2 /* Speed */, [0, 0] ); this.enabled = this.addRegister(1 /* Enabled */, [ false ]); if (loadTorque) this.loadTorque = this.addRegister( 384 /* LoadTorque */, [loadTorque] ); if (loadRotationSpeed) this.loadRotationSpeed = this.addRegister( 385 /* LoadRotationSpeed */, [loadRotationSpeed] ); } }; // src/servers/cloudadapterserver.ts var UPLOAD_JSON = "upload"; var UPLOAD_BIN = "uploadBin"; var CLOUD_COMMAND = "cloudCommand"; var CloudAdapterServer = class extends JDServiceServer { constructor(options) { super(SRV_CLOUD_ADAPTER, options); this.controlled = !!options?.controlled; this.connectedRegister = this.addRegister(384 /* Connected */, [ false ]); this.connectionNameRegister = this.addRegister( 385 /* ConnectionName */, [options?.connectionName || ""] ); this.addCommand( 128 /* UploadJson */, this.handleUpload.bind(this) ); this.addCommand( 129 /* UploadBinary */, this.handleUploadBin.bind(this) ); this.connectedRegister.on( CHANGE, () => this.sendEvent(3 /* Change */) ); this.connectionNameRegister.on( CHANGE, () => this.sendEvent(3 /* Change */) ); } get connected() { return this.connectedRegister.values()[0]; } set connected(value) { if (value !== this.connected) { this.connectedRegister.setValues([!!value]); this.sendEvent(3 /* Change */); } } async handleUpload(pkt) { if (!this.connected) { console.debug(`cloud: cancel upload, not connected`); return; } const [json] = pkt.jdunpack(CloudAdapterCmdPack.UploadJson); this.uploadJSON(json); } async handleUploadBin(pkt) { if (!this.connected) { console.debug(`cloud: cancel upload, not connected`); return; } const data = pkt.data; this.uploadBin(data); } uploadJSON(json) { this.emit(UPLOAD_JSON, { json }); } uploadBin(data) { this.emit(UPLOAD_BIN, { data }); } }; // src/servers/satnavserver.ts var SatNavServer = class extends SensorServer { constructor(options) { super( SRV_SAT_NAV, options || { streamingInterval: 1e3 } ); this.enabled = this.addRegister(1 /* Enabled */, [false]); } setGeolocationPosition(loc, skipChangeEvent) { const { timestamp: timestamp2, coords } = loc; const { latitude, longitude, accuracy, altitude, altitudeAccuracy } = coords; this.reading.setValues( [ timestamp2, latitude, longitude, accuracy, altitude || 0, altitudeAccuracy || 0 ], skipChangeEvent ); } }; function watchLocation(server, options) { let id = void 0; const success = (pos) => { console.log("geo: pos", { id, pos }); if (id !== void 0) server.setGeolocationPosition(pos); }; const unmount = () => { if (id !== void 0) navigator.geolocation.clearWatch(id); console.log("geo: unmount", { id }); id = void 0; }; const error = (err) => { console.log(err); unmount(); }; if (typeof navigator !== "undefined" && navigator.geolocation) { id = navigator.geolocation.watchPosition( success, error, options || { enableHighAccuracy: false, timeout: 5e3, maximumAge: 0 } ); console.log("geo: mount", { id }); navigator.geolocation.getCurrentPosition(success, error); } return unmount; } // src/servers/planarpositionserver.ts var PlanarPositionServer = class extends SensorServer { constructor() { super(SRV_PLANAR_POSITION, { readingValues: [0, 0], preferredStreamingInterval: 500 }); } move(dx, dy) { if (!dx && !dy) return; let [x = 0, y = 0] = this.reading.values(); x += dx; y += dy; this.reading.setValues([x, y]); this.reading.sendGetAsync(); } }; // src/servers/serialserver.ts var SerialServer = class extends JDServiceServer { constructor(options) { super(SRV_SERIAL); const { baudRate = 115200, dataBits = 8, stopBits = 1, parityMode = 0 /* None */, bufferSize = 64, connectionName } = options || {}; this.connected = this.addRegister(1 /* Connected */, [false]); this.baudRate = this.addRegister(128 /* BaudRate */, [baudRate]); this.dataBits = this.addRegister(129 /* DataBits */, [dataBits]); this.stopBits = this.addRegister(130 /* StopBits */, [stopBits]); this.parityMode = this.addRegister(131 /* ParityMode */, [parityMode]); this.bufferSize = this.addRegister(132 /* BufferSize */, [bufferSize]); if (connectionName) this.addRegister(385 /* ConnectionName */, [connectionName]); this.addCommand(128 /* Send */, this.handleSend.bind(this)); } handleSend(pkt) { const [connected] = this.connected.values(); if (!connected) return; console.debug(`serial send`, toHex2(pkt.data)); } }; // src/servers/rosserver.ts var ADVERTISE = "advertise"; var SUBSCRIBE = "subscribe"; var PUBLISH = "publish"; var RosReportMessage = 131; var RosServer = class extends JDServiceServer { constructor() { super(SRV_ROS); // topic -> nodes this._subscriptions = {}; this._messages = []; this.maxMessages = 10; this.addCommand( 130 /* SubscribeMessage */, this.handleSubscribeMessage.bind(this) ); this.addCommand( 129 /* PublishMessage */, this.handlePublishMessage.bind(this) ); } handleSubscribeMessage(pkt) { const [node, topic] = pkt.jdunpack( RosCmdPack.SubscribeMessage ); const sub = this._subscriptions[topic] || (this._subscriptions[topic] = /* @__PURE__ */ new Set()); if (!sub.has(node)) { sub.add(node); this.emit(SUBSCRIBE, { node, topic }); this.emit(CHANGE); } } /** * Clears all subscriptions */ clear() { this._subscriptions = {}; this.emit(CHANGE); } /** * Gets the list of subscription handled by this node */ get subscriptions() { return Object.keys(this._subscriptions); } /** * Gets the latest messages */ get messages() { return this._messages.slice(0); } /** * Publishes a message on the bus if any subscription is active. * @param node source node for the message * @param topic topic of the message * @param message JSON data payload; that will be converted to string */ async publishMessage(node, topic, message) { if (!this._subscriptions[topic]?.size) return; this.pushMessage({ node, topic, message }); const data = jdpack(RosCmdPack.PublishMessage, [ node, topic, JSON.stringify(message) ]); await this.sendPacketAsync(Packet.from(RosReportMessage, data)); this.emit(CHANGE); } pushMessage(msg) { this._messages.push(msg); while (this._messages.length > this.maxMessages) this._messages.unshift(); } async handlePublishMessage(pkt) { const [node, topic, messageSource] = pkt.jdunpack(RosCmdPack.PublishMessage); const message = JSONTryParse(messageSource); const msg = { node, topic, message, messageSource }; this.pushMessage(msg); this.emit(PUBLISH, msg); this.emit(CHANGE); } }; // src/servers/indexedscreenserver.ts var IndexedScreenServer = class extends JDServiceServer { constructor(options) { super(SRV_INDEXED_SCREEN, options); const { width = 8, height = 8, brightness = 1, rotation = 0, bitsPerPixel = 1, palette = [16711680, 16777215] } = options || {}; this.width = this.addRegister(385 /* Width */, [width]); this.height = this.addRegister(386 /* Height */, [height]); this.bitsPerPixel = this.addRegister(384 /* BitsPerPixel */, [ bitsPerPixel ]); this.rotation = this.addRegister(131 /* Rotation */, [rotation]); this.widthMajor = this.addRegister(129 /* WidthMajor */, [false]); const pbuf = new Uint8Array(palette.length << 2); for (let i = 0; i < palette.length; ++i) { pbuf[i * 4] = palette[i] >> 16 & 255; pbuf[i * 4 + 1] = palette[i] >> 8 & 255; pbuf[i * 4 + 2] = palette[i] & 255; pbuf[i * 4 + 3] = 255; } this.palette = this.addRegister(128 /* Palette */, [pbuf]); this.brightness = this.addRegister(1 /* Brightness */, [ brightness ]); this.addCommand( 131 /* SetPixels */, this.handleSetPixels.bind(this) ); this.addCommand( 129 /* StartUpdate */, this.handleStartUpdate.bind(this) ); this.width.skipBoundaryCheck = true; this.width.skipErrorInjection = true; this.height.skipBoundaryCheck = true; this.height.skipErrorInjection = true; this.width.on(CHANGE, this.updatePixels.bind(this)); this.height.on(CHANGE, this.updatePixels.bind(this)); this.on(FRAME_PROCESS_LARGE, this.handleLargeFrame.bind(this)); this.updatePixels(); } updatePixels() { const [width] = this.width.values(); const [height] = this.height.values(); this._clip = { x: 0, y: 0, width, height }; this._pixels = new ImageData(width, height); } get pixels() { return this._pixels; } setImage(imgData) { const [palette] = this.palette.values(); const { x, y, width, height } = this._clip; const u32palette = new Uint32Array(palette.length >> 2); for (let i = 0; i < u32palette.length; ++i) { u32palette[i] = 4278190080 | palette[i * 4] << 16 | palette[i * 4 + 1] << 8 | palette[i * 4 + 2]; } const pixdata = this._pixels.data; const data = new Uint32Array( pixdata.buffer, pixdata.byteOffset, pixdata.byteLength >> 2 ); let sp = 0; const [bpp] = this.bitsPerPixel.values(); const mask = (1 << bpp) - 1; const alignVal = bpp == 1 ? 1 : 4; for (let ix = 0; ix < width; ++ix) { for (let iy = 0; iy < height; ) { let ipix = (iy + y) * width + ix + x; const b = imgData[sp++]; for (let m = 0; m < 8; m += bpp) { data[ipix] = u32palette[b >> m & mask]; ipix += width; iy++; } } while (sp & alignVal - 1) sp++; } this.emit(CHANGE); } handleStartUpdate(pkt) { const [x, y, width, height] = pkt.jdunpack(IndexedScreenCmdPack.StartUpdate); this._clip = { x, y, width, height }; } handleLargeFrame(command, data) { if (command === "pixels") this.setImage(data); } handleSetPixels(pkt) { this.setImage(pkt.data); } }; // src/jdom/sevensegment.ts function sevenSegmentDigitEncode(value, digitCount) { const ns = isNaN(value) ? "" : value.toString(); const length = digitCount; if (isNaN(length)) return; const digits = new Uint8Array(length); const n = Math.min(length, ns.length); const digitBits = [ 63, // 0 6, // 1 91, // 2 79, // 3 102, // 4 109, // 5 125, // 6 7, // 7 127, // 8 111 // 9 ]; let k = digits.length - 1; for (let i = n - 1; i >= 0; --i) { let c = ns.charCodeAt(i); let value2 = 0; if (c == 46) { i--; if (i > -1) c = ns.charCodeAt(i); value2 |= 128; } if (c >= 48 && c < 48 + digitBits.length) value2 |= digitBits[c - 48]; else if (c == 45) value2 |= 64; else if (c == 69 || c == 101) value2 |= 79; digits[k--] = value2; } return digits; } // src/servers/sevensegmentdisplayserver.ts var SevenSegmentDisplayServer = class extends JDServiceServer { constructor(options) { super(SRV_SEVEN_SEGMENT_DISPLAY, { intensityValues: [65535] }); const { digits, decimalPoint } = options; this.digitCount = this.addRegister( 384 /* DigitCount */, [digits.length] ); this.decimalPoint = this.addRegister( 385 /* DecimalPoint */, [!!decimalPoint] ); this.digits = this.addRegister( 2 /* Digits */, [digits] ); this.addCommand( 128 /* SetNumber */, this.handleSetNumber.bind(this) ); } async handleSetNumber(pkt) { const [digitCount] = this.digitCount.values(); const [value] = pkt.jdunpack( SevenSegmentDisplayCmdPack.SetNumber ); const digits = isNaN(value) ? new Uint8Array(0) : sevenSegmentDigitEncode(value, digitCount); if (this.digits.setValues([digits])) await this.digits.sendGetAsync(); } }; // src/servers/pcmonitorserver.ts var PCMonitorServer = class extends JDServiceServer { constructor(options) { super(SRV_PCMONITOR, options); this.CPUUsage = this.addRegister(400 /* CpuUsage */, [0]); this.CPUTemperature = this.addRegister( 401 /* CpuTemperature */, [21] ); this.RAMUsage = this.addRegister(402 /* RamUsage */, [0]); this.GPUInfo = this.addRegister( 403 /* GpuInformation */, [0, 0] ); this.NetworkInfo = this.addRegister( 405 /* NetworkInformation */, [0, 0] ); } }; // src/servers/pccontrollerserver.ts var _PCControllerServer = class extends JDServiceServer { constructor(options) { super(SRV_PCCONTROLLER, options); this.addCommand(128 /* OpenUrl */, this.handleOpenUrl.bind(this)); this.addCommand( 130 /* SendText */, this.handleSendText.bind(this) ); this.addCommand( 129 /* StartApp */, this.handleStartApp.bind(this) ); this.addCommand( 131 /* RunScript */, this.handleRunScript.bind(this) ); } handleOpenUrl(pkt) { const url = pkt.stringData; this.emit(_PCControllerServer.OPEN_URL, url); } handleSendText(pkt) { const text = pkt.stringData; this.emit(_PCControllerServer.SEND_TEXT, text); } handleStartApp(pkt) { const text = pkt.stringData; this.emit(_PCControllerServer.START_APP, text); } handleRunScript(pkt) { const text = pkt.stringData; this.emit(_PCControllerServer.RUN_SCRIPT, text); } }; var PCControllerServer = _PCControllerServer; PCControllerServer.OPEN_URL = "openUrl"; PCControllerServer.SEND_TEXT = "sendText"; PCControllerServer.START_APP = "startApp"; PCControllerServer.RUN_SCRIPT = "runScript"; // src/servers/servers.ts var indoorThermometerOptions = { readingValues: [21.5], streamingInterval: 1e3, minReading: -5, maxReading: 50, readingError: [0.25], variant: 2 /* Indoor */ }; var outdoorThermometerOptions = { readingValues: [21.5], streamingInterval: 1e3, minReading: -40, maxReading: 120, readingError: [0.25], variant: 1 /* Outdoor */ }; var outdoorHumidityOptions = { streamingInterval: 1e3, readingValues: [40], readingError: [0.1], minReading: 10, maxReading: 99 }; var soilThermometerOptions = { readingValues: [15], streamingInterval: 1e3, minReading: -55, maxReading: 125, readingError: [0.5], variant: 1 /* Outdoor */ }; var medicalThermometerOptions = { readingValues: [37.5], streamingInterval: 1e3, minReading: 35, maxReading: 42, readingError: [0.5], variant: 3 /* Body */ }; var barometerOptions = { readingValues: [1013], readingError: [1.5], streamingInterval: 1e3, minReading: 150, maxReading: 4e3 }; var sonarOptions = { variant: 1 /* Ultrasonic */, minReading: 0.02, maxReading: 4, readingValues: [1] }; var SG90_STALL_TORQUE = 1.8; var SG90_RESPONSE_SPEED = 0.12; var microServoOptions = { stallTorque: SG90_STALL_TORQUE, // kg/cm responseSpeed: SG90_RESPONSE_SPEED, // s/60deg minAngle: 0, maxAngle: 180 }; var microServoContinuousOptions = { stallTorque: SG90_STALL_TORQUE, // kg/cm responseSpeed: SG90_RESPONSE_SPEED, // s/60deg minAngle: -90, maxAngle: 90, clientVariant: "cont=1" }; var microServo270Options = { stallTorque: SG90_STALL_TORQUE, // kg/cm responseSpeed: SG90_RESPONSE_SPEED, // s/60deg minAngle: 0, maxAngle: 270 }; var microServo360Options = { stallTorque: SG90_STALL_TORQUE, // kg/cm responseSpeed: SG90_RESPONSE_SPEED * 2, // s/60deg minAngle: 0, maxAngle: 360 }; var windDirectionOptions = { readingValues: [0], readingError: [5], streamingInterval: 5e3 }; var windSpeedOptions = { readingValues: [0], readingError: [2], streamingInterval: 5e3, registerValues: [{ code: 261 /* MaxWindSpeed */, values: [80] }] }; var eCO2Options = { readingValues: [400], streamingInterval: 1e3, variant: 1 /* VOC */ }; var CO2Options = { readingValues: [400], streamingInterval: 1e3, variant: 2 /* NDIR */ }; var tvocOptions = { readingValues: [500], streamingInterval: 1e3 }; var microbitSounds = [ [0, "giggle"], [0, "happy"], [0, "hello"], [0, "mysterious"], [0, "sad"], [0, "slide"], [0, "soaring"], [0, "spring"], [0, "twinkle"], [0, "yawn"] ]; var soundLevel = { readingValues: [0], intensityValues: [false] }; var soundSpectrum = { readingValues: [new Uint8Array(0)], intensityValues: [false], registerValues: [ { code: 128 /* FftPow2Size */, values: [5] }, { code: 129 /* MinDecibels */, values: [-100] }, { code: 130 /* MaxDecibels */, values: [-30] }, { code: 131 /* SmoothingTimeConstant */, values: [0.8] } ] }; var _providerDefinitions; function initProviders() { return _providerDefinitions = _providerDefinitions || [ { name: "7-segment (4 segments)", serviceClasses: [SRV_SEVEN_SEGMENT_DISPLAY], services: () => [ new SevenSegmentDisplayServer({ digits: fromHex("00000000") }) ] }, { name: "7-segment (8 segments)", serviceClasses: [SRV_SEVEN_SEGMENT_DISPLAY], services: () => [ new SevenSegmentDisplayServer({ digits: fromHex("0000000000000000") }) ] }, { name: "accelerometer", serviceClasses: [SRV_ACCELEROMETER], services: () => [new AccelerometerServer()] }, { name: "water acidity (pH)", serviceClasses: [SRV_ACIDITY], services: () => [ new AnalogSensorServer(SRV_ACIDITY, { minReading: 2.5, maxReading: 10, readingValues: [7], readingError: [0.1], readingResolution: 0.1 }) ] }, { name: "barometer", serviceClasses: [SRV_AIR_PRESSURE], services: () => [ new AnalogSensorServer(SRV_AIR_PRESSURE, barometerOptions) ] }, { name: "bitradio", serviceClasses: [SRV_BIT_RADIO], services: () => [new BitRadioServer()] }, { name: "Braille display (4 patterns)", serviceClasses: [SRV_BRAILLE_DISPLAY], services: () => [ new BrailleDisplayServer({ patterns: "\u2803", length: 4 }) ] }, { name: "Braille display (16 patterns)", serviceClasses: [SRV_BRAILLE_DISPLAY], services: () => [ new BrailleDisplayServer({ patterns: "\u2803", length: 16 }) ] }, { name: "Braille display (32 patterns)", serviceClasses: [SRV_BRAILLE_DISPLAY], services: () => [ new BrailleDisplayServer({ patterns: "\u2803", length: 32 }) ] }, { name: "button", serviceClasses: [SRV_BUTTON], services: () => [new ButtonServer()] }, { name: "button (2x)", serviceClasses: [SRV_BUTTON], services: () => [ new ButtonServer("B0"), new ButtonServer("B1") ] }, { name: "button (4x)", serviceClasses: [SRV_BUTTON], services: () => Array(4).fill(0).map((_, i) => new ButtonServer(`B${i}`)) }, { name: "buzzer", serviceClasses: [SRV_BUZZER], services: () => [new BuzzerServer()] }, { name: "capacitive button", serviceClasses: [SRV_BUTTON], services: () => { const button = new ButtonServer(); const config = new CapacitiveButtonServer(); button.threshold = config.threshold; return [button, config]; } }, { name: "capacitive button (6x)", serviceClasses: [SRV_BUTTON], services: () => Array(6).fill(0).map((_, i) => new ButtonServer(`C${i}`, true)) }, { name: "capacitive button (12x)", serviceClasses: [SRV_BUTTON], services: () => Array(12).fill(0).map((_, i) => new ButtonServer(`C${i}`, true)) }, { name: "character screen (LDC, 16x2)", serviceClasses: [SRV_CHARACTER_SCREEN], services: () => [ new CharacterScreenServer({ message: "hello\nworld!" }) ] }, { name: "character screen (OLED, 32x8, RTL)", serviceClasses: [SRV_CHARACTER_SCREEN], services: () => [ new CharacterScreenServer({ message: "hello\nworld!" }) ], serviceOptions: [ { serviceClass: SRV_CHARACTER_SCREEN, constants: { columns: 32, rows: 9, variant: 2 /* OLED */, textDirection: 2 /* RightToLeft */ } } ] }, { name: "color", serviceClasses: [SRV_COLOR], services: () => [ new SensorServer(SRV_COLOR, { readingValues: [0.5, 0, 0.5], preferredStreamingInterval: 1e3 }) ] }, { name: "compass", serviceClasses: [SRV_COMPASS], services: () => [new CompassServer()] }, { name: "DC current/voltage measurement", serviceClasses: [ SRV_DC_CURRENT_MEASUREMENT, SRV_DC_VOLTAGE_MEASUREMENT ], services: () => [ new AnalogSensorServer(SRV_DC_CURRENT_MEASUREMENT, { readingValues: [1e-3], readingResolution: 1e-3, streamingInterval: 100, minReading: 0, maxReading: 1, registerValues: [ { code: 386 /* MeasurementName */, values: ["amp"] } ] }), new AnalogSensorServer(SRV_DC_VOLTAGE_MEASUREMENT, { readingValues: [5], streamingInterval: 100, readingResolution: 1e-3, minReading: 0, maxReading: 7, registerValues: [ { code: 386 /* MeasurementName */, values: ["volt"] }, { code: 385 /* MeasurementType */, values: [ 0 /* Absolute */ ] } ] }) ] }, { name: "distance (sonar)", serviceClasses: [SRV_DISTANCE], services: () => [ new AnalogSensorServer(SRV_DISTANCE, sonarOptions) ] }, { name: "DMX", serviceClasses: [SRV_DMX], services: () => [new DMXServer()] }, { name: "eCO\u2082", serviceClasses: [SRV_E_CO2], services: () => [ new AnalogSensorServer(SRV_E_CO2, eCO2Options) ] }, { name: "eCO\u2082 + TVOC", serviceClasses: [SRV_E_CO2, SRV_TVOC], services: () => [ new AnalogSensorServer(SRV_E_CO2, eCO2Options), new AnalogSensorServer(SRV_TVOC, tvocOptions) ] }, { name: "eCO\u2082 + humidity + thermometer", serviceClasses: [SRV_E_CO2, SRV_HUMIDITY, SRV_TEMPERATURE], services: () => [ new AnalogSensorServer(SRV_E_CO2, CO2Options), new AnalogSensorServer( SRV_HUMIDITY, outdoorHumidityOptions ), new AnalogSensorServer( SRV_TEMPERATURE, indoorThermometerOptions ) ] }, { name: "flex sensor", serviceClasses: [SRV_FLEX], services: () => [ new AnalogSensorServer(SRV_FLEX, { readingValues: [0.5] }) ] }, { name: "gyroscope", serviceClasses: [SRV_GYROSCOPE], services: () => [ new SensorServer(SRV_GYROSCOPE, { readingValues: [0, 0, 0] }) ] }, { name: "heart rate", serviceClasses: [SRV_HEART_RATE], services: () => [ new AnalogSensorServer(SRV_HEART_RATE, { readingValues: [80], streamingInterval: 100, variant: 1 /* Finger */ }) ] }, { name: "humidity", serviceClasses: [SRV_HUMIDITY], services: () => [ new AnalogSensorServer( SRV_HUMIDITY, outdoorHumidityOptions ) ] }, { name: "humidity + temperature", serviceClasses: [SRV_HUMIDITY, SRV_TEMPERATURE], services: () => [ new AnalogSensorServer( SRV_TEMPERATURE, outdoorThermometerOptions ), new AnalogSensorServer( SRV_HUMIDITY, outdoorHumidityOptions ) ] }, { name: "humidity + temperature + barometer", serviceClasses: [ SRV_HUMIDITY, SRV_TEMPERATURE, SRV_AIR_PRESSURE ], services: () => [ new AnalogSensorServer( SRV_TEMPERATURE, outdoorThermometerOptions ), new AnalogSensorServer( SRV_HUMIDITY, outdoorHumidityOptions ), new AnalogSensorServer(SRV_AIR_PRESSURE, barometerOptions) ] }, { name: "HID keyboard (simulated)", serviceClasses: [SRV_HID_KEYBOARD], services: () => [new HIDKeyboardServer()] }, { name: "HID joystick (simulated)", serviceClasses: [SRV_HID_JOYSTICK], services: () => [new HIDJoystickServer()] }, { name: "HID mouse (simulated)", serviceClasses: [SRV_HID_MOUSE], services: () => [new HIDMouseServer()] }, { name: "illuminance", serviceClasses: [SRV_ILLUMINANCE], services: () => [ new AnalogSensorServer(SRV_ILLUMINANCE, { readingValues: [1] }) ] }, { name: "gamepad (stick + A + B)", serviceClasses: [SRV_GAMEPAD], services: () => [ new GamepadServer({ variant: 1 /* Thumb */, buttonsAvailable: 16 /* A */ | 32 /* B */ }) ] }, { name: "gamepad (stick)", serviceClasses: [SRV_GAMEPAD], services: () => [ new GamepadServer({ variant: 1 /* Thumb */ }) ] }, { name: "gamepad (stick+A)", serviceClasses: [SRV_GAMEPAD], services: () => [ new GamepadServer({ variant: 1 /* Thumb */, buttonsAvailable: 16 /* A */ }) ] }, { name: "gamepad (Dpad + arcade buttons)", serviceClasses: [SRV_GAMEPAD], services: () => [ new GamepadServer({ variant: 4 /* Gamepad */, buttonsAvailable: GAMEPAD_ARCADE_BUTTONS }) ] }, { name: "gamepad (only DPad+A/B)", serviceClasses: [SRV_GAMEPAD], services: () => [ new GamepadServer({ variant: 4 /* Gamepad */, buttonsAvailable: GAMEPAD_DPAD_AB_BUTTONS }) ] }, { name: "gamepad (Dpad + all buttons)", serviceClasses: [SRV_GAMEPAD], services: () => [ new GamepadServer({ variant: 4 /* Gamepad */, buttonsAvailable: GAMEPAD_ARCADE_BUTTONS | 1024 /* X */ | 2048 /* Y */ }) ] }, { name: "geolocation (satelitte navigation)", serviceClasses: [SRV_SAT_NAV], services: () => [new SatNavServer()] }, { name: "LED ring 8 pixels", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 8, variant: 2 /* Ring */ }) ] }, { name: "LED ring 10 pixels", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 10, variant: 2 /* Ring */ }) ] }, { name: "LED ring 12 pixels", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 12, variant: 2 /* Ring */ }) ] }, { name: "LED ring 16 pixels", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 16, variant: 2 /* Ring */ }) ] }, { name: "LED ring 24 pixels", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 24, variant: 2 /* Ring */ }) ] }, { name: "LED jewel 7 pixels", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 7, variant: 4 /* Jewel */ }) ] }, { name: "LED stick 8", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 8, variant: 3 /* Stick */ }) ] }, { name: "LED single", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 1, ledsPerPixel: 1, color: [255, 0, 0], variant: 3 /* Stick */ }) ] }, { name: "LED single with 5 leds", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 1, waveLength: 450, ledsPerPixel: 5, color: [0, 0, 255], variant: 3 /* Stick */ }) ] }, { name: "LED matrix (5x5 micro:bit)", serviceClasses: [SRV_DOT_MATRIX], services: () => [ new DotMatrixServer(5, 5, { brightness: 128, variant: 1 /* LED */ }) ] }, { name: "LED matrix (8x8)", serviceClasses: [SRV_DOT_MATRIX], services: () => [ new DotMatrixServer(8, 8, { brightness: 128, variant: 1 /* LED */ }) ] }, { name: "LED matrix (11x7)", serviceClasses: [SRV_DOT_MATRIX], services: () => [ new DotMatrixServer(11, 7, { brightness: 128, variant: 1 /* LED */ }) ] }, { name: "Braille matrix (8x4)", serviceClasses: [SRV_DOT_MATRIX], services: () => [ new DotMatrixServer(8, 4, { variant: 2 /* Braille */ }) ] }, { name: "LED pixel strip 30", serviceClasses: [SRV_LED_STRIP], services: () => [ new LedStripServer({ numPixels: 60, maxPower: 1e3, variant: 1 /* Strip */ }) ] }, { name: "LED pixel strip 60", serviceClasses: [SRV_LED_STRIP], services: () => [ new LedStripServer({ numPixels: 60, maxPower: 2e3, variant: 1 /* Strip */ }) ] }, { name: "LED pixel strip 150", serviceClasses: [SRV_LED_STRIP], services: () => [ new LedStripServer({ numPixels: 150, maxPower: 5e3, variant: 1 /* Strip */ }) ] }, { name: "LED pixel strip 300", serviceClasses: [SRV_LED_STRIP], services: () => [ new LedStripServer({ numPixels: 300, maxPower: 5e3, variant: 1 /* Strip */ }) ] }, { name: "LED pixel matrix (4x4)", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 16, variant: 5 /* Matrix */ }) ] }, { name: "LED pixel matrix (8x8)", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 64, variant: 5 /* Matrix */, numColumns: 8 }) ] }, { name: "LED pixel matrix (16x4)", serviceClasses: [SRV_LED], services: () => [ new LedServer({ numPixels: 64, numColumns: 16, variant: 5 /* Matrix */ }) ] }, { name: "light bulb", serviceClasses: [SRV_LIGHT_BULB], services: () => [ new JDServiceServer(SRV_LIGHT_BULB, { intensityValues: [0], isActive: (values2) => !!values2?.[0], intensityProcessor: (values2) => { const newValues = [values2[0] > 0 ? 1 : 0]; return newValues; }, registerValues: [ { code: 384 /* Dimmable */, values: [false] } ] }) ] }, { name: "light bulb (dimmeable)", serviceClasses: [SRV_LIGHT_BULB], services: () => [ new JDServiceServer(SRV_LIGHT_BULB, { intensityValues: [0], isActive: (values2) => !!values2?.[0], registerValues: [ { code: 384 /* Dimmable */, values: [true] } ] }) ] }, { name: "light level (solar)", serviceClasses: [SRV_LIGHT_LEVEL], services: () => [ new SensorServer(SRV_LIGHT_LEVEL, { readingValues: [0.5], variant: 1 /* PhotoResistor */ }) ] }, { name: "line sensor (digital)", serviceClasses: [SRV_REFLECTED_LIGHT], services: () => [new ReflectedLightServer()] }, { name: "line sensor (2x digital)", serviceClasses: [SRV_REFLECTED_LIGHT], services: () => [ new ReflectedLightServer(), new ReflectedLightServer() ] }, { name: "line sensor (3x digital)", serviceClasses: [SRV_REFLECTED_LIGHT], services: () => [ new ReflectedLightServer(), new ReflectedLightServer(), new ReflectedLightServer() ] }, { name: "line sensor (analog)", serviceClasses: [SRV_REFLECTED_LIGHT], services: () => [ new ReflectedLightServer({ variant: 2 /* InfraredAnalog */ }) ] }, { name: "magnetic field level", serviceClasses: [SRV_MAGNETIC_FIELD_LEVEL], services: () => [ new MagneticFieldLevelServer({ variant: 1 /* AnalogNS */ }) ] }, { name: "matrix keypad (3x4)", serviceClasses: [SRV_MATRIX_KEYPAD], services: () => [ new MatrixKeypadServer(3, 4, [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "*", "0", "#" ]) ] }, { name: "matrix keypad (4x4)", serviceClasses: [SRV_MATRIX_KEYPAD], services: () => [ new MatrixKeypadServer(4, 4, [ "0", "1", "2", "A", "3", "4", "5", "B", "6", "7", "8", "C", "*", "0", "#", "D" ]) ] }, { name: "matrix keypad (1x4)", serviceClasses: [SRV_MATRIX_KEYPAD], services: () => [ new MatrixKeypadServer(4, 1, ["1", "2", "3", "4"]) ] }, { name: "motion", serviceClasses: [SRV_MOTION], services: () => [ new SensorServer(SRV_MOTION, { readingValues: [false], streamingInterval: 1e3 }) ] }, { name: "motor", serviceClasses: [SRV_MOTOR], services: () => [new MotorServer()], resetIn: true }, { name: "motor (dual)", serviceClasses: [SRV_DUAL_MOTORS], services: () => [new DualMotorsServer()], resetIn: true }, { name: "PC controller", serviceClasses: [SRV_PCCONTROLLER], services: () => [new PCControllerServer()] }, { name: "PC monitor", serviceClasses: [SRV_PCMONITOR], services: () => [new PCMonitorServer()] }, { name: "planar position", serviceClasses: [SRV_PLANAR_POSITION], services: () => [new PlanarPositionServer()] }, Flags.diagnostics && { name: "protocol test", serviceClasses: [SRV_PROTO_TEST], services: () => [new ProtocolTestServer()] }, { name: "pulse oxymeter", serviceClasses: [SRV_PULSE_OXIMETER], services: () => [ new SensorServer(SRV_PULSE_OXIMETER, { readingValues: [98], streamingInterval: 1e3 }) ] }, { name: "power supply (5v)", serviceClasses: [SRV_POWER_SUPPLY], services: () => [ new PowerSupplyServer({ outputVoltage: 1e3, minVoltage: 0, maxVoltage: 5e3 }) ] }, { name: "oxymeter + heart beat", serviceClasses: [SRV_PULSE_OXIMETER, SRV_HEART_RATE], services: () => [ new SensorServer(SRV_PULSE_OXIMETER, { readingValues: [98], streamingInterval: 1e3 }), new AnalogSensorServer(SRV_HEART_RATE, { readingValues: [80], streamingInterval: 1e3, variant: 1 /* Finger */ }) ] }, { name: "power", serviceClasses: [SRV_POWER], services: () => [new PowerServer()] }, { name: "RNG (random number generator)", serviceClasses: [SRV_RNG], services: () => [new RandomNumberGeneratorServer()] }, { name: "rain gauge", serviceClasses: [SRV_RAIN_GAUGE], services: () => [new RainGaugeServer()] }, { name: "real time clock", serviceClasses: [SRV_REAL_TIME_CLOCK], services: () => [new RealTimeClockServer()] }, { name: "relay (EM/10A)", serviceClasses: [SRV_RELAY], services: () => [ new JDServiceServer(SRV_RELAY, { intensityValues: [false], isActive: (values2) => !!values2?.[0], variant: 1 /* Electromechanical */, registerValues: [ { code: 384 /* MaxSwitchingCurrent */, values: [10] } ] }) ] }, { name: "relay 4x (SSR/5A)", serviceClasses: [SRV_RELAY], services: () => Array(4).fill(0).map( () => new JDServiceServer(SRV_RELAY, { intensityValues: [false], isActive: (values2) => !!values2?.[0], variant: 2 /* SolidState */, registerValues: [ { code: 384 /* MaxSwitchingCurrent */, values: [5] } ] }) ) }, { name: "rotary encoder", serviceClasses: [SRV_ROTARY_ENCODER], services: () => [new RotaryEncoderServer()] }, { name: "rotary encoder + button", serviceClasses: [SRV_ROTARY_ENCODER, SRV_BUTTON], services: () => [new RotaryEncoderServer(), new ButtonServer()] }, { name: "rotary potentiometer", serviceClasses: [SRV_POTENTIOMETER], services: () => [ new AnalogSensorServer(SRV_POTENTIOMETER, { variant: 2 /* Rotary */, readingValues: [0.5] }) ] }, { name: "serial (115200/8N1)", serviceClasses: [SRV_SERIAL], services: () => [new SerialServer()] }, { name: "servo", serviceClasses: [SRV_SERVO], services: () => [new ServoServer(microServoOptions)], resetIn: true }, { name: "servo (270\xB0)", serviceClasses: [SRV_SERVO], services: () => [new ServoServer(microServo270Options)], resetIn: true }, { name: "servo (360\xB0)", serviceClasses: [SRV_SERVO], services: () => [new ServoServer(microServo360Options)], resetIn: true }, { name: "servo x 2", serviceClasses: [SRV_SERVO], resetIn: true, services: () => Array(2).fill(0).map( (_, i) => new ServoServer({ ...microServoOptions, instanceName: `S${i}` }) ) }, { name: "servo x 4", serviceClasses: [SRV_SERVO], resetIn: true, services: () => Array(4).fill(0).map( (_, i) => new ServoServer({ ...microServoOptions, instanceName: `S${i}` }) ) }, { name: "servo x 6", serviceClasses: [SRV_SERVO], resetIn: true, services: () => Array(6).fill(0).map( (_, i) => new ServoServer({ ...microServoOptions, instanceName: `S${i}` }) ) }, { name: "servo x 16", serviceClasses: [SRV_SERVO], resetIn: true, services: () => Array(16).fill(0).map( (_, i) => new ServoServer({ ...microServoOptions, instanceName: `S${i}` }) ) }, { name: "servo (continuous)", serviceClasses: [SRV_SERVO], services: () => [new ServoServer(microServoContinuousOptions)], resetIn: true }, { name: "servo (continuous) x2", serviceClasses: [SRV_SERVO], services: () => Array(2).fill(0).map( (_, i) => new ServoServer({ ...microServoContinuousOptions, instanceName: `S${i}` }) ), resetIn: true }, { name: "settings", serviceClasses: [SRV_SETTINGS], services: () => [new SettingsServer()] }, { name: "slider (potentiometer)", serviceClasses: [SRV_POTENTIOMETER], services: () => [ new AnalogSensorServer(SRV_POTENTIOMETER, { variant: 1 /* Slider */ }) ] }, { name: "Hall sensor (potentiometer)", serviceClasses: [SRV_POTENTIOMETER], services: () => [ new AnalogSensorServer(SRV_POTENTIOMETER, { variant: 3 /* Hall */ }) ] }, { name: "soil moisture", serviceClasses: [SRV_SOIL_MOISTURE], services: () => [ new AnalogSensorServer(SRV_SOIL_MOISTURE, { readingValues: [0.5], readingError: [0.05], streamingInterval: 1e3 }) ] }, { name: "speech synthesis", serviceClasses: [SRV_SPEECH_SYNTHESIS], services: () => [new SpeechSynthesisServer()] }, { name: "solenoid", serviceClasses: [SRV_SOLENOID], services: () => [ new JDServiceServer(SRV_SOLENOID, { intensityValues: [0] }) ] }, { name: "sound level", serviceClasses: [SRV_SOUND_LEVEL], services: () => [ new AnalogSensorServer(SRV_SOUND_LEVEL, soundLevel) ] }, { name: "sound spectrum", serviceClasses: [SRV_SOUND_SPECTRUM], services: () => [ new SensorServer( SRV_SOUND_SPECTRUM, soundSpectrum ) ] }, { name: "sound player (micro:bit V2 sounds)", serviceClasses: [SRV_SOUND_PLAYER], services: () => [new SoundPlayerServer(microbitSounds)] }, { name: "switch (slide)", serviceClasses: [SRV_SWITCH], services: () => [ new SwitchServer({ variant: 1 /* Slide */ }) ] }, { name: "switch (push button)", serviceClasses: [SRV_SWITCH], services: () => [ new SwitchServer({ variant: 3 /* PushButton */ }) ] }, { name: "switch (toggle)", serviceClasses: [SRV_SWITCH], services: () => [ new SwitchServer({ variant: 5 /* Toggle */ }) ] }, { name: "switch (tilt)", serviceClasses: [SRV_SWITCH], services: () => [ new SwitchServer({ variant: 2 /* Tilt */ }) ] }, { name: "switch (proximity)", serviceClasses: [SRV_SWITCH], services: () => [ new SwitchServer({ variant: 6 /* Proximity */ }) ] }, { name: "thermometer (outdoor)", serviceClasses: [SRV_TEMPERATURE], services: () => [ new AnalogSensorServer( SRV_TEMPERATURE, outdoorThermometerOptions ) ] }, { name: "thermometer (soil)", serviceClasses: [SRV_TEMPERATURE], services: () => [ new AnalogSensorServer( SRV_TEMPERATURE, soilThermometerOptions ) ] }, { name: "thermometer (medical)", serviceClasses: [SRV_TEMPERATURE], services: () => [ new AnalogSensorServer( SRV_TEMPERATURE, medicalThermometerOptions ) ] }, { name: "traffic light", serviceClasses: [SRV_TRAFFIC_LIGHT], services: () => [new TrafficLightServer()] }, { name: "traffic crossing (4 x lights)", serviceClasses: [SRV_TRAFFIC_LIGHT], services: () => Array(4).fill(0).map(() => new TrafficLightServer()) }, { name: "TVOC", serviceClasses: [SRV_TVOC], services: () => [new AnalogSensorServer(SRV_TVOC, tvocOptions)] }, { name: "UV index", serviceClasses: [SRV_UV_INDEX], services: () => [ new AnalogSensorServer(SRV_UV_INDEX, { readingValues: [5], minReading: 0, maxReading: 11, streamingInterval: 1e3 }) ] }, { name: "vibration motor", serviceClasses: [SRV_VIBRATION_MOTOR], services: () => [new VibrationMotorServer()] }, { name: "water level", serviceClasses: [SRV_WATER_LEVEL], services: () => [ new AnalogSensorServer(SRV_WATER_LEVEL, { readingValues: [0.5], streamingInterval: 1e3 }) ] }, { name: "water pump (relay)", serviceClasses: [SRV_RELAY], services: () => [ new JDServiceServer(SRV_RELAY, { intensityValues: [false], isActive: (values2) => !!values2?.[0], variant: 1 /* Electromechanical */, registerValues: [ { code: 384 /* MaxSwitchingCurrent */, values: [10] } ] }) ] }, { name: "weight scale (jewelry)", serviceClasses: [SRV_WEIGHT_SCALE], services: () => [ new AnalogSensorServer(SRV_WEIGHT_SCALE, { readingValues: [1e-3], variant: 3 /* Jewelry */, maxReading: 0.2, minReading: 5e-4, readingResolution: 1e-5 }) ] }, { name: "weight scale (body)", serviceClasses: [SRV_WEIGHT_SCALE], services: () => [ new AnalogSensorServer(SRV_WEIGHT_SCALE, { readingValues: [60], variant: 1 /* Body */, maxReading: 180, readingResolution: 0.1 }) ] }, { name: "weight scale (food)", serviceClasses: [SRV_WEIGHT_SCALE], services: () => [ new AnalogSensorServer(SRV_WEIGHT_SCALE, { readingValues: [0.5], variant: 2 /* Food */, maxReading: 6, readingResolution: 1e-3 }) ] }, { name: "wind direction", serviceClasses: [SRV_WIND_DIRECTION], services: () => [ new AnalogSensorServer( SRV_WIND_DIRECTION, windDirectionOptions ) ] }, { name: "wind speed", serviceClasses: [SRV_WIND_SPEED], services: () => [ new AnalogSensorServer(SRV_WIND_SPEED, windSpeedOptions) ] }, { name: "weather station (wind speed, direction, rain)", serviceClasses: [ SRV_WIND_SPEED, SRV_WIND_DIRECTION, SRV_RAIN_GAUGE ], services: () => [ new AnalogSensorServer(SRV_WIND_SPEED, windSpeedOptions), new AnalogSensorServer( SRV_WIND_DIRECTION, windDirectionOptions ), new RainGaugeServer() ] }, { name: "chassis (motor x 2 + sonar + light)", serviceClasses: [SRV_DISTANCE, SRV_LED, SRV_MOTOR], services: () => [ new MotorServer("L"), new MotorServer("R"), new AnalogSensorServer(SRV_DISTANCE, sonarOptions), new LedServer({ numPixels: 5, variant: 3 /* Stick */ }) ] }, { name: "railway crossing (2 x lights, 2 x servos, 1 x buffer)", serviceClasses: [SRV_TRAFFIC_LIGHT, SRV_SERVO, SRV_BUZZER], services: () => [ new TrafficLightServer({ instanceName: "left light" }), new ServoServer({ minAngle: 0, maxAngle: 90, instanceName: "left arm" }), new TrafficLightServer({ instanceName: "right light" }), new ServoServer({ minAngle: 0, maxAngle: 90, instanceName: "right arm" }), new BuzzerServer({ instanceName: "bell" }) ] }, { name: "Arcade controller (6 x buttons)", serviceClasses: [SRV_BUTTON], services: () => [ new ButtonServer("Left"), new ButtonServer("Up"), new ButtonServer("Right"), new ButtonServer("Down"), new ButtonServer("A"), new ButtonServer("B") ] }, { name: "micro:bit V2", serviceClasses: [ SRV_DOT_MATRIX, SRV_BUTTON, SRV_ACCELEROMETER, SRV_SOUND_LEVEL, SRV_LIGHT_LEVEL, SRV_BUZZER, SRV_SOUND_PLAYER ], services: () => [ new DotMatrixServer(5, 5), new ButtonServer("A"), new ButtonServer("B"), new SensorServer( SRV_ACCELEROMETER, { readingValues: [ 0.5, 0.5, -(1 - (0.5 * 0.5 + 0.5 * 0.5)) ] } ), new AnalogSensorServer(SRV_SOUND_LEVEL, soundLevel), new SensorServer(SRV_LIGHT_LEVEL, { readingValues: [0.5], variant: 2 /* ReverseBiasedLED */ }), new BuzzerServer(), new SoundPlayerServer(microbitSounds) ] }, { name: "power + humidity", serviceClasses: [SRV_POWER, SRV_HUMIDITY], services: () => [ new PowerServer(), new AnalogSensorServer( SRV_HUMIDITY, outdoorHumidityOptions ) ], factory: (services) => { const dev = new JDServerServiceProvider("power+humidity", [ services[0] ]); const pwr = dev.service(1); pwr.allowed.on(CHANGE, () => { const allowed = !!pwr.allowed.values()[0]; console.debug(`power: ${allowed ? "on" : "off"}`); if (allowed) dev.updateServices(services); else dev.updateServices([services[0]]); }); return dev; } }, { name: "Cloud adapter (simulator)", serviceClasses: [SRV_CLOUD_ADAPTER], services: () => [ new CloudAdapterServer({ connectionName: "simulated" }) ] }, { name: "ROS (simulator)", serviceClasses: [SRV_ROS], services: () => [new RosServer()] }, { name: "Display 128x64 monochrome (1bpp)", serviceClasses: [SRV_INDEXED_SCREEN], services: () => [ new IndexedScreenServer({ width: 128, height: 64, bitsPerPixel: 1, palette: [0, 16777215] }) ] }, { name: "Display 128x128 16colors (4bpp)", serviceClasses: [SRV_INDEXED_SCREEN], services: () => [ new IndexedScreenServer({ width: 128, height: 128, bitsPerPixel: 4, palette: [ 0, 16777215, 16720161, 16749508, 16744757, 16774665, 2399395, 7920722, 16301, 8909567, 9318084, 10781599, 6045804, 15060420, 9520701, 0 ] }) ] }, Flags.diagnostics ? { name: "WiFi (virtual, no ap)", serviceClasses: [SRV_WIFI], services: () => [new WifiServer()] } : void 0, Flags.diagnostics ? { name: "WiFi (virtual, 1 AP)", serviceClasses: [SRV_WIFI], services: () => [ new WifiServer({ scanResults: [ { ssid: "HOME", bssid: new Uint8Array(0), rssi: -42, channel: 10, flags: 2 /* WPS */ | 256 /* IEEE_802_11B */ } ] }) ] } : void 0, Flags.diagnostics ? { name: "WiFi (virtual, 1 network)", serviceClasses: [SRV_WIFI], services: () => [ new WifiServer({ scanResults: [ { ssid: "HOME", bssid: new Uint8Array(0), rssi: -42, channel: 10, flags: 2 /* WPS */ | 256 /* IEEE_802_11B */ } ], knownNetworks: [ { ssid: "HOME", password: "home", priority: 0, flags: 2 /* WPS */ | 256 /* IEEE_802_11B */ } ] }) ] } : void 0 ].filter((s) => !!s); } function serviceProviderDefinitions() { return initProviders().slice(0); } function addServiceProviderDefinition(def) { const providers = initProviders(); if (!providers.find((p) => p.name === def.name)) providers.push(def); } function fingerPrint() { try { if (typeof self !== "undefined" && self.localStorage) { const key = "jacdac_device_fingerprint"; const f = self.localStorage[key] || (self.localStorage[key] = randomDeviceId()); return f; } } catch (e) { return ""; } } function stableSimulatorDeviceId(bus, template, salt) { const fg = fingerPrint(); const others = bus.serviceProviders().filter((sp) => sp.template === template); const word0 = hash(stringToUint8Array(salt + template + others.length), 32); const word1 = hash( stringToUint8Array(salt + fg + template + others.length + 1), 32 ); const id = toFullHex([word0, word1]); return id.slice(2); } function applyServiceOptions(services, serviceOptions) { serviceOptions?.forEach(({ serviceClass: serviceClass2, serviceOffset, constants }) => { const srvs = services.filter((srv) => srv.serviceClass === serviceClass2); const service = srvs[serviceOffset || 0]; if (!service) { console.warn( `service provider: service 0x${serviceClass2.toString( 16 )} not found at offset ${serviceOffset || 0}`, { srvs } ); } else { const { specification } = service; Object.entries(constants).forEach(([name, value]) => { const spec = specification.packets.find( (pkt) => isConstRegister(pkt) && pkt.name === name ); if (!spec) console.warn( `service provider: unknown register ${specification.name}.${name}` ); else { const reg = service.register(spec.identifier); if (!reg) service.addRegister(spec.identifier, [value]); else reg.setValues([value]); } }); } }); } function addServiceProvider(bus, definition, serviceOptions) { const services = definition.services(); applyServiceOptions(services, definition.serviceOptions); applyServiceOptions(services, serviceOptions); services.forEach((srv) => srv.lock()); const salt = bus.serviceProviderIdSalt; const deviceId = stableSimulatorDeviceId(bus, definition.name, salt); const options = { resetIn: definition.resetIn, deviceId, deviceDescription: definition.name }; const d = definition.factory?.(services) || new JDServerServiceProvider(definition.name, services, options); bus.addServiceProvider(d); return d; } function addServer(bus, name, server, serviceOptions) { const services = [server]; return addServiceProvider( bus, { name, serviceClasses: services.map((srv) => srv.serviceClass), services: () => services }, serviceOptions ); } function serviceProviderDefinitionFromServiceClass(serviceClass2) { return initProviders().find( (provider) => provider.serviceClasses.length === 1 && provider.serviceClasses[0] === serviceClass2 ); } function syntheticServiceProvider(bus, serviceClass2) { const specification = serviceSpecificationFromClassIdentifier(serviceClass2); if (!specification) return void 0; const { name } = specification; let server; if (isSensor(specification)) { const reading = specification.packets.find(isReading); if (reading.fields.length === 1 && isNumericType(reading.fields[0])) { const field = reading.fields[0]; const { min, max, scale = 1, defl } = genFieldInfo(reading, field); const value = typeof defl === "number" ? defl : (max - min) / 2; server = new AnalogSensorServer(serviceClass2, { readingValues: [value / scale], minReading: min / scale, maxReading: max / scale }); } } else if (isActuator(specification)) { const intensity = 0; const valueReg = specification.packets.find( (pkt) => pkt.identifier === 2 /* Value */ ); const { min, max, scale = 1, defl } = valueReg && genFieldInfo(valueReg, valueReg.fields[0]) || {}; const value = typeof defl === "number" ? defl : (max - min) / 2; server = new JDServiceServer(serviceClass2, { intensityValues: [intensity], valueValues: !isNaN(value) ? [value / scale] : void 0 }); } return server && { name, serviceClasses: [serviceClass2], services: () => [server] }; } function startServiceProviderFromServiceClass(bus, serviceClass2) { const provider = serviceProviderDefinitionFromServiceClass(serviceClass2) || syntheticServiceProvider(bus, serviceClass2); return provider ? addServiceProvider(bus, provider) : void 0; } // src/jdom/serviceclient.ts var JDServiceClient = class extends JDClient { constructor(service) { super(); this.service = service; const statusCodeChanged = this.service.event( 4 /* StatusCodeChanged */ ); this.mount(statusCodeChanged?.subscribe(EVENT, () => this.emit(CHANGE))); } get device() { return this.service.device; } get bus() { return this.device.bus; } get statusCode() { const reg = this.service.register(259 /* StatusCode */); return reg.unpackedValue?.[0]; } toString() { return `client of ${this.service}`; } }; // src/jdom/clients/rolemanagerclient.ts function resolveRoleService(bus, role) { const { deviceId, serviceIndex, serviceClass: serviceClass2 } = role; if (!deviceId || isNaN(serviceIndex)) return void 0; const device = bus.device(deviceId, true); const service = device?.service(serviceIndex); if (service && service.serviceClass !== serviceClass2) { console.warn("unexpected service class for role", { role }); return void 0; } return service; } function parentName(bus, role) { if (role.query) { const args = role.query.split("&").map((a) => a.split("=", 2)); const deviceId = args.find((a) => a[0] === ROLE_QUERY_DEVICE)?.[1]; if (deviceId === ROLE_QUERY_SELF_DEVICE) return bus.selfDeviceId; return deviceId; } return role.name.split("/", 1)[0]; } function parseRole(role) { const specification = serviceSpecificationFromClassIdentifier( role.serviceClass ); if (!specification) return void 0; const args = role.query?.split("&").map((a) => a.split("=", 2)).filter(([n, v]) => n && v !== void 0).map(([n, v]) => ({ name: n.toLowerCase().trim(), value: v })); const serviceOffset = args?.filter((arg) => arg.name === ROLE_QUERY_SERVICE_OFFSET).map((arg) => { const i = parseInt(arg.value); return isNaN(i) ? void 0 : i; })[0]; const serviceIndex = args?.filter((arg) => arg.name === ROLE_QUERY_SERVICE_INDEX).map((arg) => { const i = parseInt(arg.value); return isNaN(i) ? void 0 : i; })[0]; const REG_NAME_MAP = { name: "instance_name" }; const pktArgs = args?.map(({ name, value }) => ({ name: REG_NAME_MAP[name] || name, value }))?.map(({ name, value }) => ({ name, value, pkt: specification.packets.find( (pkt) => isConstRegister(pkt) && pkt.name === name ) })).filter((a) => !!a.pkt?.packFormat).map(({ name, value, pkt }) => { let simpleValue; const type = pkt.fields[0].type; const enumType = specification.enums?.[type]; if (enumType) simpleValue = enumType.members[value] || parseInt(value); else if (type == "string") simpleValue = value; else simpleValue = parseInt(value); return { name, value: simpleValue }; }); if (serviceOffset === void 0 && serviceIndex === void 0 && !pktArgs?.length) return void 0; const constants = pktArgs && toMap( pktArgs, (a) => a.name, (a) => a.value ); const r = { serviceClass: role.serviceClass, serviceOffset, serviceIndex, constants }; console.debug(`role: ${role.name}`, r); return r; } var RoleManagerClient = class extends JDServiceClient { constructor(service) { super(service); this._roles = []; this._needRefresh = true; this._lastRefreshAttempt = 0; this.changeEvent = service.event(3 /* Change */); this.startRefreshRoles = debounceAsync( this.refreshRoles.bind(this), 200 ); this.mount( this.changeEvent.subscribe(EVENT, this.handleChange.bind(this)) ); this.mount( this.bus.subscribe(DEVICE_ANNOUNCE, this.assignRoles.bind(this)) ); this.mount(this.clearRoles.bind(this)); this.mount( this.bus.subscribe( SELF_ANNOUNCE, this.handleSelfAnnounce.bind(this) ) ); } /** * @hidden */ toString() { return `role manager ${this.service.toString()}`; } handleSelfAnnounce() { if (this._needRefresh && this.bus.timestamp - this._lastRefreshAttempt > ROLE_MANAGER_POLL) this.startRefreshRoles(); } get roles() { return this._roles; } async handleChange() { this.startRefreshRoles(); } async refreshRoles() { if (this.unmounted) return; this._needRefresh = false; await this.collectRoles(); if (this.unmounted) return; this.assignRoles(); } async collectRoles() { this._lastRefreshAttempt = this.bus.timestamp; const previousRolesHash = JSON.stringify(this._roles); try { const inp = new InPipeReader(this.bus); await this.service.sendPacketAsync( inp.openCommand(131 /* ListRoles */), true ); const roles = []; for (const buf of await inp.readData(1500)) { const [devidbuf, serviceClass2, serviceIndex, full] = jdunpack(buf, "b[8] u32 u8 s"); const deviceId = toHex2(devidbuf); const [name, query] = full.split("?", 2); const role = { deviceId, serviceClass: serviceClass2, serviceIndex, name, query }; roles.push(role); } if (JSON.stringify(roles) !== previousRolesHash) { this.log(`roles updated`, roles); this._roles = roles; this.emit(CHANGE); } } catch (e) { this.log(`collect roles failed`); this._needRefresh = true; this.emit(ERROR, e); } } assignRoles() { this.bus.services().filter((srv) => !isInfrastructure(srv.specification)).forEach((srv) => this.assignRole(srv)); } assignRole(service) { const deviceId = service.device.deviceId; const serviceIndex = service.serviceIndex; const role = this._roles.find( (r) => r.deviceId === deviceId && r.serviceIndex === serviceIndex ); if (service.role !== role?.name) this.log(`role ${service} -> ${role?.name || ""}`, { role }); service.role = role?.name; } clearRoles() { this.bus.services().forEach((srv) => srv.role = void 0); } hasRoleForService(service) { const { serviceClass: serviceClass2 } = service; return !!this._roles?.find((r) => r.serviceClass === serviceClass2); } compatibleRoles(service) { const { serviceClass: serviceClass2 } = service; return this._roles?.filter((r) => r.serviceClass === serviceClass2); } role(name) { return this._roles.find((r) => r.serviceIndex > 0 && r.name === name); } async setRole(service, name) { const { device, serviceIndex } = service; const { deviceId } = device; const previous = name && this._roles.find((r) => r.name === name); if (previous && previous.deviceId === deviceId && previous.serviceIndex === serviceIndex) { this.log(`role unmodified, skipping`); return; } { this.log(`assign role ${deviceId}[${serviceIndex}] -> ${name}`); const data = jdpack("b[8] u8 s", [ fromHex(deviceId), serviceIndex, name || "" ]); await this.service.sendPacketAsync( Packet.from(129 /* SetRole */, data), true ); } if (previous && previous.deviceId != "0000000000000000") { this.log(`clear role ${previous.deviceId}:${previous.serviceIndex}`); const data = jdpack("b[8] u8 s", [ fromHex(previous.deviceId), previous.serviceIndex, "" ]); await this.service.sendPacketAsync( Packet.from(129 /* SetRole */, data), true ); } } allRolesBound() { return this._roles.every((role) => !!this.bus.device(role.deviceId, true)); } startSimulators() { this.log(`start role sims`, { roles: this._roles }); const roles = this._roles.filter( (role) => !this.bus.device(role.deviceId, true) ); if (!roles?.length) return; this.log(`unbound roles: ${roles.length}`, { roles }); const todos = groupBy( roles.map((role) => ({ role, hostDefinition: serviceProviderDefinitionFromServiceClass( role.serviceClass ) })).filter((todo) => !!todo.hostDefinition), (todo) => parentName(this.bus, todo.role) || "" ); this.log(`simulateable roles`, todos); const parents = Object.keys(todos); parents.forEach((parent) => { const todo = todos[parent]; if (!parent) { todo.forEach((t) => { const serviceOptions = parseRole(t.role); addServiceProvider( this.bus, t.hostDefinition, serviceOptions ? [serviceOptions] : void 0 ); }); } else { addServiceProvider( this.bus, { name: "", serviceClasses: [], services: () => arrayConcatMany( todo.map((t) => t.hostDefinition.services()) ) }, todo.map((t) => parseRole(t.role)).filter((q) => !!q) ); } }); } }; // src/jdom/scheduler.ts var WallClockScheduler = class { constructor() { this._now = typeof performance !== "undefined" ? () => performance.now() : () => Date.now(); this._startTime = this._now(); } get timestamp() { return this._now() - this._startTime; } resetTime(delta = 0) { this._startTime = this._now() - delta; } setTimeout(handler, delay2, ...args) { return setTimeout(handler, delay2, args); } clearTimeout(handle) { clearTimeout(handle); } setInterval(handler, delay2, ...args) { return setInterval(handler, delay2, args); } clearInterval(handle) { clearInterval(handle); } }; // jacdac-spec/dist/devices.json var devices_default = [ { id: "yahboom-microbitledv10", name: "Microbit-LED", company: "Yahboom", description: "The expansion board integrates 24 programmable RGB lights and microphones, and you can control the on-board RGB lights by the microphone.", makeCodeRepo: [ { target: "microbit", name: "yahboom-microbitledv10", slug: "pelikhan/yahboom-microbit-led-jacdac" } ], connector: "noConnector", link: "https://category.yahboom.net/products/rgbledb", storeLink: [ "https://category.yahboom.net/products/rgbledb" ], services: [ 369743088, 346888797 ], productIdentifiers: [ 978358865 ], version: "1.0", status: "stable" }, { id: "yahboom-tinybitv13", name: "Tiny-Bit", company: "Yahboom", description: "Tiny:bit is a robotic car for the micro:bit education market. It is compact, easy to assemble, and easy to move in tight spaces. The Tiny:bit smart car is based on the micro:bit development board design and uses the online code programming of MakeCode Editor. Rich sensor applications allow Tiny:bit for easy interaction. The Tiny:bit smart car has a set of alligator clips on the rear that can be creative and expand.", makeCodeRepo: [ { target: "microbit", name: "yahboom-tinybitv13", slug: "pelikhan/Tiny-bitLib/tree/master/jacdac" } ], connector: "noConnector", link: "https://category.yahboom.net/products/tinybit", storeLink: [ "https://category.yahboom.net/products/tinybit" ], services: [ 355063095, 369743088, 369743088, 309087410, 309087410, 337275786, 346888797 ], productIdentifiers: [ 878674793 ], version: "1.3", status: "stable" }, { id: "unexpected-maker-feathers2esp32s2v20", name: "FeatherS2 ESP32-S2", company: "Unexpected Maker", description: "The full-featured ESP32-S2 based development board in a Feather format from Unexpected Maker. The FeatherS2 is a power house, fully souped up with 16 MB of Flash memory (for firmware and file storage) and 8 MB of QSPI-based external PSRAM so you can have massive storage buffers.", repo: "https://github.com/microsoft/devicescript-esp32", firmwareSource: "https://github.com/microsoft/devicescript-esp32/blob/main/boards/esp32s2/feather_s2.board.json", connector: "qwiic", link: "https://feathers2.io/", storeLink: [ "https://unexpectedmaker.com/shop/feathers2-esp32-s2" ], services: [ 413852154, 342028028, 341864092, 471386691, 343122531, 400333340 ], productIdentifiers: [ 824637191 ], version: "2.0", status: "stable" }, { id: "stmicroelectronics-bl475eiot01adevicescriptv00", name: "B-L475E-IOT01A DeviceScript", company: "STMicroelectronics", description: "B-L475E-IOT01A running Jacdac firmware accessing built-in sensors and DeviceScript. Built with AzureRTOS.", firmwareSource: "https://github.com/RLeclair/jacdac-azure-rtos/tree/master/STMicroelectronics/B-L475E-IOT01A", connector: "noConnector", link: "https://github.com/RLeclair/jacdac-azure-rtos/tree/master/STMicroelectronics/B-L475E-IOT01A", storeLink: [ "https://www.st.com/en/evaluation-tools/b-l475e-iot01a.html" ], services: [ 337754823, 521405449, 505087730, 382210232, 504462570, 288680491 ], productIdentifiers: [ 954528034 ], version: "0.0", status: "stable" }, { id: "sparkfun-gamerbitv00", name: "gamer:bit", company: "Sparkfun", description: 'The SparkFun gamer:bit is a fun-filled "carrier" board for the micro:bit that, when combined with the micro:bit, provides you with a fully functional game system. Designed in a similar form factor to the classic Nintendo NES controller, the gamer:bit is equipped with a four-direction "D-pad" on the left side of the board and two action buttons on the right side of the board. The two push buttons on the micro:bit in the center function as start and select.', makeCodeRepo: [ { target: "microbit", name: "sparkfun-gamerbitv00", slug: "pelikhan/pxt-gamer-bit/tree/master/jacdac" } ], connector: "noConnector", link: "https://www.sparkfun.com/products/retired/14215", storeLink: [ "https://www.sparkfun.com/products/retired/14215" ], services: [ 277836886 ], productIdentifiers: [ 929407584 ], version: "0.0", status: "stable" }, { id: "seeed-studio-grove16x2lcdv20", name: "Grove-16x2 LCD", company: "Seeed Studio", description: "Seeedstudio Grove - 16 x 2 LCD is a I2C LCD display. 16x2 means two lines and each line has 16 columns, 32 characters in total.", connector: "grove", link: "https://wiki.seeedstudio.com/Grove-16x2_LCD_Series/", storeLink: [ "https://www.seeedstudio.com/Grove-16-x-2-LCD-Black-on-Yellow.html" ], services: [ 523748714 ], productIdentifiers: [ 877317536 ], version: "2.0", status: "stable" }, { id: "seeed-studio-relayv12", name: "Relay", company: "Seeed Studio", description: "The Grove-Relay module is a digital normally-open switch. Through it, you can control circuit of high voltage with low voltage, 3.3-5V on the controller. There is an indicator LED on the board, which will light up when the controlled terminals get closed.", connector: "grove", link: "https://wiki.seeedstudio.com/Grove-Relay/", storeLink: [ "https://www.seeedstudio.com/Grove-Relay.html" ], services: [ 406840918 ], productIdentifiers: [ 808015809 ], version: "1.2", status: "stable" }, { id: "seeed-studio-soilmoisturev14", name: "Soil Moisture", company: "Seeed Studio", description: "Soil Moisture Sensor can measure soil moisture for plants. The soil moisture sensor consists of two probes that allow the current to pass through the soil and then obtain resistance values to measure soil moisture content.", hardwareDesign: "https://wiki.seeedstudio.com/Grove-Moisture_Sensor/", connector: "grove", link: "https://www.seeedstudio.com/Grove-Moisture-Sensor.html", storeLink: [ "https://www.seeedstudio.com/Grove-Moisture-Sensor.html" ], services: [ 491430835 ], productIdentifiers: [ 814817380 ], version: "1.4", status: "stable" }, { id: "seeed-studio-ultrasonicdistancesensorv20", name: "Ultrasonic Distance Sensor", company: "Seeed Studio", description: "This Grove - Ultrasonic ranger is a non-contact distance measurement module which works at 40KHz. When we provide a pulse trigger signal with more than 10uS through signal pin, the Grove_Ultrasonic_Ranger will issue 8 cycles of 40kHz cycle level and detect the echo. The pulse width of the echo signal is proportional to the measured distance.", connector: "grove", link: "https://www.seeedstudio.com/Grove-Ultrasonic-Distance-Sensor.html", storeLink: [ "https://www.seeedstudio.com/Grove-Ultrasonic-Distance-Sensor.html" ], services: [ 337275786 ], productIdentifiers: [ 1068863164 ], version: "2.0", status: "stable" }, { id: "seeed-studio-xiaoesp32c3v00", name: "XIAO ESP32C3", company: "Seeed Studio", description: "A tiny dev-board with ESP32-C3 from Seeed.", repo: "https://github.com/microsoft/devicescript-esp32", firmwareSource: "https://github.com/microsoft/devicescript-esp32/blob/main/boards/esp32c3/seeed_xiao_esp32c3.board.json", connector: "noConnector", storeLink: [ "https://www.seeedstudio.com/Seeed-XIAO-ESP32C3-p-5431.html" ], services: [ 413852154, 342028028, 341864092, 343122531 ], productIdentifiers: [ 1056926545 ], version: "0.0", status: "stable" }, { id: "roger-wagner-makerbitv10", name: "MakerBit", company: "Roger Wagner", description: "The MakerBit board provides simple and efficient connections to the features of the BBC micro:bit. Easily and quickly add lights and touch sensors to school projects, models and art installations. There are enough connecting wires and LEDs for one project. The MakerBit can control a maximum of 12 LEDs and 12 touch sensors in any one project.", makeCodeRepo: [ { target: "microbit", name: "roger-wagner-makerbitv10-motor", slug: "1010Technologies/pxt-makerbit-motor/jacdac" }, { target: "microbit", name: "roger-wagner-makerbitv10-touch", slug: "1010Technologies/pxt-makerbit-touch/jacdac" }, { target: "microbit", name: "roger-wagner-makerbitv10-lcd1602", slug: "1010Technologies/pxt-makerbit-lcd1602/jacdac" } ], connector: "grove", link: "https://makerbit.com/", storeLink: [ "https://makerbit.com/" ], services: [ 385895640, 343122531, 523748714 ], productIdentifiers: [ 1018020097 ], version: "1.0", status: "stable" }, { id: "raspberry-pi-picov00", name: "Pico", company: "Raspberry Pi", description: "Raspberry Pi Pico is a low-cost, high-performance microcontroller board with flexible digital interfaces.", repo: "https://github.com/microsoft/devicescript-pico", firmwareSource: "https://github.com/microsoft/devicescript-pico/blob/main/boards/rp2040/pico.board.json", connector: "noConnector", link: "https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html", storeLink: [ "https://www.raspberrypi.com/products/raspberry-pi-pico/" ], productIdentifiers: [ 1064181516 ], version: "0.0", status: "stable" }, { id: "raspberry-pi-picowv00", name: "Pico W", company: "Raspberry Pi", description: "Raspberry Pi Pico is a low-cost, high-performance microcontroller board with flexible digital interfaces. Raspberry Pi Pico W adds on-board single-band 2.4GHz wireless interfaces (802.11n) using the Infineon CYW43439 while retaining the Pico form factor. ", repo: "https://github.com/microsoft/devicescript-pico", firmwareSource: "https://github.com/microsoft/devicescript-pico/blob/main/boards/rp2040w/pico_w.board.json", connector: "noConnector", link: "https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html", storeLink: [ "https://www.raspberrypi.com/products/raspberry-pi-pico/" ], services: [ 413852154, 342028028, 341864092, 471386691, 343122531, 400333340 ], productIdentifiers: [ 978399748 ], version: "0.0", status: "stable" }, { id: "microbit-educational-foundation-microbitv2", name: "micro:bit V2", company: "microbit Educational Foundation", description: "The new micro:bit V2 has a built-in microphone and speaker to allow sound-sensing and sound-making without the need to attach another device. It also introduces capacitive touch sensing, a power-saving mode and more computing power for the classroom.", connector: "noConnector", link: "https://microsoft.github.io/jacdac-docs/clients/makecode/microbit-jukebox/", storeLink: [ "https://www.microbit.org/buy/" ], productIdentifiers: [ 854992189, 888455511, 1035770297, 1049819173 ], transport: { type: "usb", requestDescription: "BBC micro:bit CMSIS-DAP" }, firmwares: [ { name: "Micro:bit Jukebox", url: "https://github.com/microsoft/pxt-jacdac/releases/latest/download/microbit-jukebox.hex", productIdentifier: 888455511 }, { name: "Micro:bit MicroCode", url: "https://microsoft.github.io/microcode/assets/firmware.hex", productIdentifier: 1049819173 } ], bootloader: { driveName: "MICROBIT", firmwareFormat: "hex" }, status: "stable", relatedDevices: [ "kittenbot-jacdaptorformicrobitv2v10" ] }, { id: "maker-go-esp32c3superminiv10", name: "ESP32-C3 SuperMini", company: "Maker go", description: "A tiny ESP32-C3 development board.", connector: "noConnector", link: "https://usa.banggood.com/ESP32-C3-Development-Board-ESP32-SuperMini-WiFi-Bluetooth-Mini-Module-p-1997449.html", storeLink: [ "https://usa.banggood.com/ESP32-C3-Development-Board-ESP32-SuperMini-WiFi-Bluetooth-Mini-Module-p-1997449.html" ], services: [ 288680491, 413852154 ], productIdentifiers: [ 1024631722 ], version: "1.0", status: "stable" }, { id: "kittenbot-accelerometerv10", name: "Accelerometer", company: "KittenBot", description: "Accelerometer sensors are devices used to measure acceleration or motion.", storeLink: [ "https://www.kittenbot.cc/products/jacdac-kit-b-elite-module-suite-redefining-electronic-interfacing" ], services: [ 521405449 ], productIdentifiers: [ 1031216668 ], version: "1.0", status: "stable", shape: "ec30_2x2_lr" }, { id: "kittenbot-agilewhiskerkeyboardelite60v10", name: "AgileWhisker Keyboard Elite60", company: "KittenBot", description: "This is the first mechanical keyboard in the Jacdac system, featuring a 60% layout, a rotary encoder, RGB LED lighting, and two Jacdac interfaces. It is powered by the RP2040 microcontroller.", connector: "edgeHighCurrentProvider", link: "https://w.kittenbot.net/", storeLink: [ "https://w.kittenbot.net/" ], services: [ 309264608, 414210922, 437330261, 411425820, 284830153 ], productIdentifiers: [ 878230570 ], version: "1.0", status: "stable" }, { id: "kittenbot-agilewhiskernumerickeypadv10", name: "AgileWhisker Numeric Keypad", company: "KittenBot", description: "This is the first numeric keypad in the Jacdac system, featuring an OLED screen, a rotary encoder, RGB LED lighting, and one Jacdac interface. It is powered by the RP2040 microcontroller.", connector: "edgeLowCurrentProviderConsumer", link: "https://w.kittenbot.net/", storeLink: [ "https://w.kittenbot.net/" ], services: [ 309264608, 414210922, 437330261, 411425820, 284830153, 523748714 ], productIdentifiers: [ 988197346 ], version: "1.0", status: "stable" }, { id: "kittenbot-awagentv10", name: "AW Agent", company: "KittenBot", description: "AW Agent, short for AgileWhisker Agent, is a small software for PC computers that is designed to be used in conjunction with AgileWhisker Keyboard Elite60 and other keyboards. It is an open-source software. It functions as a virtual hardware within the jacdac ecosystem. It incorporates MQTT services to retrieve hardware information from the computer and trigger computer events, among other functionalities.", connector: "noConnector", link: "https://w.kittenbot.net/", storeLink: [ "https://w.kittenbot.net/" ], services: [ 457422603, 289210942 ], productIdentifiers: [ 1020385313 ], version: "1.0", status: "stable" }, { id: "kittenbot-duckybotkitv10", name: "DuckyBot Kit", company: "KittenBot", description: " Dive into the enchanting world of robotics with DuckyBot, a delightful cardboard robot shaped like a cheerful yellow duck. Engineered around the groundbreaking Jacdac Elite Module Suite, DuckyBot offers young learners a quirky and entertaining gateway into the vast realm of electronics and robotics, integrating seamlessly with Microbit and Makecode platforms.", storeLink: [ "https://www.kittenbot.cc/products/duckybot-crafting-coding-and-quacking-with-robotics" ], tags: [ "kit", "microbit" ], version: "1.0", status: "stable", devices: [ "kittenbot-powerv10", "kittenbot-hapticoutputv10", "kittenbot-accelerometerv10", "kittenbot-relayv10", "kittenbot-envsensorv10", "kittenbot-rgbstripv10", "kittenbot-servov10", "kittenbot-ultrasonicsensorv10", "kittenbot-hubv10", "kittenbot-jacdaptorformicrobitv2v10", "microbit-educational-foundation-microbitv2" ], relatedDevices: [ "microbit-educational-foundation-microbitv2" ] }, { id: "kittenbot-envsensorv10", name: "Env Sensor", company: "KittenBot", description: "Env Sensor is a device used to measure both the temperature and humidity of the surrounding environment.", storeLink: [ "https://www.kittenbot.cc/products/jacdac-kit-b-elite-module-suite-redefining-electronic-interfacing" ], services: [ 337754823, 382210232 ], productIdentifiers: [ 998683858 ], version: "1.0", status: "stable", shape: "ec30_2x2_lr" }, { id: "kittenbot-grapebitv10", name: "Grape:bit", company: "KittenBot", description: "Grape:bit is a programming main control board mainly for the second to third grade information technology learning and institutional teaching. Its core is the ESP32, which has the characteristics of high performance, wireless communication, and graphical programming.", repo: "https://github.com/KittenBot/devicescript-esp32/tree/grapebit", firmwareSource: "https://github.com/KittenBot/devicescript-esp32/blob/grapebit/boards/esp32c3/kitten_grapebit_c3.board.json", connector: "edgeLowCurrentProviderConsumer", storeLink: [ "https://www.kittenbot.cc/products/kittenbot-grapebit-20-pack" ], services: [ 288680491, 521405449, 343122531, 458731991, 282614377, 385895640 ], productIdentifiers: [ 952937357 ], transport: { type: "serial", vendorId: 4292 }, firmwares: [ { name: "Grape:bit", url: "https://github.com/KittenBot/devicescript-esp32/releases/latest", productIdentifier: 952937357 } ], version: "1.0", status: "stable" }, { id: "kittenbot-hapticoutputv10", name: "Haptic Output", company: "KittenBot", description: "The Haptic Output module is essentially a vibration motor that can be used to provide haptic feedback in electronic devices.", storeLink: [ "https://www.kittenbot.cc/products/jacdac-kit-b-elite-module-suite-redefining-electronic-interfacing" ], services: [ 406832290 ], productIdentifiers: [ 837852806 ], version: "1.0", status: "stable", shape: "ec30_2x2_lr" }, { id: "kittenbot-hubv10", name: "Hub", company: "KittenBot", description: "A passthrough cable hub.", connector: "edgePassive", storeLink: [ "https://www.amazon.com/KittenBot-Jacdac-Starter-Jacdaptor-Micro/dp/B0BQ6WMBPZ", "https://www.kittenbot.cc/collections/frontpage/products/kittenbot-jacdac-kit-for-micro-bit", "https://www.aliexpress.com/item/3256804237465484.html", "https://www.pakronics.com.au/products/kittenbot-jacdac-kit-a-with-adaptor-for-micro-bit-v2-pakr-a0410" ], tags: [ "hub" ], version: "1.0", status: "stable" }, { id: "kittenbot-jacdacdeveloperstoolelectronicmodulekitbv10", name: "Jacdac Developer's-tool Electronic module Kit B", company: "KittenBot", description: "Dive deep into the forefront of electronic interfacing with the Jacdac Elite Module Suite. Built on the bedrock of Microsoft's avant-garde Jacdac protocol, this suite aggregates an array of meticulously crafted modules, setting the gold standard for next-gen electronic projects.\n\nIt Includes:\n\nJacdac-Power X1\nJacdac-Haptic Output X1\nJacdac-Accelerometer X1\nJacdac-Relay X1\nJacdac-Env Sensor X1\nJacdac-RGB Strip X1\nJacdac-Servo X1\nJacdac-Ultrasonic X1\nJacdac-Hub_B X1\nJacdac Cable 10 cm X2\nJacdac Cable 35 cm X2\nJacdac Cable 100 cm X1", storeLink: [ "https://www.kittenbot.cc/products/jacdac-kit-b-elite-module-suite-redefining-electronic-interfacing" ], tags: [ "kit" ], version: "1.0", status: "stable", devices: [ "kittenbot-powerv10", "kittenbot-hapticoutputv10", "kittenbot-accelerometerv10", "kittenbot-relayv10", "kittenbot-envsensorv10", "kittenbot-rgbstripv10", "kittenbot-servov10", "kittenbot-ultrasonicsensorv10", "kittenbot-hubv10" ], relatedDevices: [ "kittenbot-jacdacstarterkitawithjacdaptorformicrobitv2v10", "kittenbot-brainrp2040v10" ] }, { id: "kittenbot-jacdacstarterkitawithjacdaptorformicrobitv2v10", name: "Jacdac Starter Kit A with Jacdaptor for micro:bit V2", company: "KittenBot", description: "A kit of Jacdac modules.", storeLink: [ "https://www.amazon.com/KittenBot-Jacdac-Starter-Jacdaptor-Micro/dp/B0BQ6WMBPZ", "https://www.kittenbot.cc/collections/frontpage/products/kittenbot-jacdac-kit-for-micro-bit", "https://www.aliexpress.com/item/3256804237465484.html", "https://www.pakronics.com.au/products/kittenbot-jacdac-kit-a-with-adaptor-for-micro-bit-v2-pakr-a0410" ], tags: [ "kit", "microbit" ], version: "1.0", status: "stable", devices: [ "kittenbot-keycapbuttonv10", "kittenbot-lightsensorv10", "kittenbot-magnetsensorv10", "kittenbot-rotarybuttonv10", "kittenbot-sliderv10", "kittenbot-rgbringv10", "kittenbot-hubv10", "kittenbot-jacdaptorformicrobitv2v10" ], relatedDevices: [ "microbit-educational-foundation-microbitv2", "kittenbot-jacdacdeveloperstoolelectronicmodulekitbv10", "kittenbot-brainrp2040v10" ], requiredDevices: [ "microbit-educational-foundation-microbitv2" ], order: 1 }, { id: "kittenbot-jacdaptorformicrobitv2v10", name: "Jacdaptor for micro:bit V2", company: "KittenBot", connector: "edgeLowCurrentProviderConsumer", storeLink: [ "https://www.amazon.com/KittenBot-Jacdac-Starter-Jacdaptor-Micro/dp/B0BQ6WMBPZ", "https://www.kittenbot.cc/collections/frontpage/products/kittenbot-jacdac-kit-for-micro-bit", "https://www.aliexpress.com/item/3256804237465484.html", "https://www.pakronics.com.au/products/kittenbot-jacdac-kit-a-with-adaptor-for-micro-bit-v2-pakr-a0410" ], tags: [ "adapter", "microbit" ], version: "1.0", status: "stable", relatedDevices: [ "microbit-educational-foundation-microbitv2" ], requiredDevices: [ "microbit-educational-foundation-microbitv2" ], shape: "ec30_1x7_r7" }, { id: "kittenbot-keycapbuttonv10", name: "Keycap Button", company: "KittenBot", description: "A keycap keyboard button.", storeLink: [ "https://www.amazon.com/KittenBot-Jacdac-Starter-Jacdaptor-Micro/dp/B0BQ6WMBPZ", "https://www.kittenbot.cc/collections/frontpage/products/kittenbot-jacdac-kit-for-micro-bit", "https://www.aliexpress.com/item/3256804237465484.html", "https://www.pakronics.com.au/products/kittenbot-jacdac-kit-a-with-adaptor-for-micro-bit-v2-pakr-a0410" ], services: [ 343122531 ], productIdentifiers: [ 926152985 ], version: "1.0", status: "stable", shape: "ec30_3x2_lr" }, { id: "kittenbot-lightsensorv10", name: "Light Sensor", company: "KittenBot", description: "A light level sensor.", storeLink: [ "https://www.amazon.com/KittenBot-Jacdac-Starter-Jacdaptor-Micro/dp/B0BQ6WMBPZ", "https://www.kittenbot.cc/collections/frontpage/products/kittenbot-jacdac-kit-for-micro-bit", "https://www.aliexpress.com/item/3256804237465484.html", "https://www.pakronics.com.au/products/kittenbot-jacdac-kit-a-with-adaptor-for-micro-bit-v2-pakr-a0410" ], services: [ 400333340 ], productIdentifiers: [ 973683863, 1017391393 ], version: "1.0", status: "stable", shape: "ec30_2x2_lr" }, { id: "kittenbot-magnetsensorv10", name: "Magnet Sensor", company: "KittenBot", description: "A magnetic field sensor.", storeLink: [ "https://www.amazon.com/KittenBot-Jacdac-Starter-Jacdaptor-Micro/dp/B0BQ6WMBPZ", "https://www.kittenbot.cc/collections/frontpage/products/kittenbot-jacdac-kit-for-micro-bit", "https://www.aliexpress.com/item/3256804237465484.html", "https://www.pakronics.com.au/products/kittenbot-jacdac-kit-a-with-adaptor-for-micro-bit-v2-pakr-a0410" ], services: [ 318642191 ], productIdentifiers: [ 881631743, 1018441315 ], version: "1.0", status: "stable", shape: "ec30_2x2_lr" }, { id: "kittenbot-nanoscript2040v10", name: "NanoScript 2040", company: "KittenBot", description: "Elevate your embedded projects with our cutting-edge development board, powered by Raspberry Pi's RP2040 and tailored for DeviceScript. Experience the unmatched synergy of TypeScript in the realm of microcontroller programming, where precision, efficiency, and modern development practices converge.", connector: "edgeLowCurrentProviderConsumer", storeLink: [ "https://www.kittenbot.cc/products/devicescript-enhanced-development-board-with-rp2040" ], services: [ 411425820, 414210922, 524302175 ], productIdentifiers: [ 935525573 ], version: "1.0", status: "stable", shape: "ec30_3x2_r" }, { id: "kittenbot-powerv10", name: "Power", company: "KittenBot", description: "Power module is used to supply high-current JACDAC modules such as servo modules.It can also be used to supply power to the entire JACDAC electronic system.It has overcurrent protection and a maximum output current of 1A.", connector: "edgeHighCurrentProvider", storeLink: [ "https://www.kittenbot.cc/products/jacdac-kit-b-elite-module-suite-redefining-electronic-interfacing" ], services: [ 530893146 ], productIdentifiers: [ 1015787249 ], version: "1.0", status: "stable", shape: "ec30_2x4_l2" }, { id: "kittenbot-relayv10", name: "Relay", company: "KittenBot", description: "This relay module is designed for low voltage and low current applications. It's recommended to use a voltage of 5V and a current no greater than 1A.\nWhen using a relay module, it's important to be aware of electrical safety. It's recommended that you use the relay module under adult supervision, especially if you are not familiar with electrical circuits and how to use them safely.", storeLink: [ "https://www.kittenbot.cc/products/jacdac-kit-b-elite-module-suite-redefining-electronic-interfacing" ], services: [ 406840918 ], productIdentifiers: [ 830413461 ], version: "1.0", status: "stable", shape: "ec30_2x2_l" }, { id: "kittenbot-rgbringv10", name: "RGB Ring", company: "KittenBot", description: "A ring of 8 colored programmable LEDs.", storeLink: [ "https://www.amazon.com/KittenBot-Jacdac-Starter-Jacdaptor-Micro/dp/B0BQ6WMBPZ", "https://www.kittenbot.cc/collections/frontpage/products/kittenbot-jacdac-kit-for-micro-bit", "https://www.aliexpress.com/item/3256804237465484.html", "https://www.pakronics.com.au/products/kittenbot-jacdac-kit-a-with-adaptor-for-micro-bit-v2-pakr-a0410" ], services: [ 369743088 ], productIdentifiers: [ 924243657 ], version: "1.0", status: "stable", shape: "ec30_3x3_l" }, { id: "kittenbot-rgbstripv10", name: "RGB Strip", company: "KittenBot", description: "RGB Strip module is used to drive WS2812 LED strips. If you're driving a large number of LEDs, such as more than 10, it's recommended to provide additional power to the LED strip to ensure stable and reliable operation.", storeLink: [ "https://www.kittenbot.cc/products/jacdac-kit-b-elite-module-suite-redefining-electronic-interfacing" ], services: [ 309264608 ], productIdentifiers: [ 1033734884 ], version: "1.0", status: "stable", shape: "ec30_2x2_l" }, { id: "kittenbot-rotarybuttonv10", name: "Rotary Button", company: "KittenBot", description: "A rotary encoder and button.", storeLink: [ "https://www.amazon.com/KittenBot-Jacdac-Starter-Jacdaptor-Micro/dp/B0BQ6WMBPZ", "https://www.kittenbot.cc/collections/frontpage/products/kittenbot-jacdac-kit-for-micro-bit", "https://www.aliexpress.com/item/3256804237465484.html", "https://www.pakronics.com.au/products/kittenbot-jacdac-kit-a-with-adaptor-for-micro-bit-v2-pakr-a0410" ], services: [ 284830153, 343122531 ], productIdentifiers: [ 984959466 ], version: "1.0", status: "stable", shape: "ec30_3x2_lr" }, { id: "kittenbot-servov10", name: "Servo", company: "KittenBot", description: "Servo module is used to drive standard 3-wire analog servos. It has two output channels and can operate in two different current output modes: 500mA and 1A.", connector: "edgeLowCurrentProvider", storeLink: [ "https://www.kittenbot.cc/products/jacdac-kit-b-elite-module-suite-redefining-electronic-interfacing" ], services: [ 318542083 ], productIdentifiers: [ 918447378 ], version: "1.0", status: "stable", shape: "ec30_2x3_l" }, { id: "kittenbot-sliderv10", name: "Slider", company: "KittenBot", description: "A slider.", storeLink: [ "https://www.amazon.com/KittenBot-Jacdac-Starter-Jacdaptor-Micro/dp/B0BQ6WMBPZ", "https://www.kittenbot.cc/collections/frontpage/products/kittenbot-jacdac-kit-for-micro-bit", "https://www.aliexpress.com/item/3256804237465484.html", "https://www.pakronics.com.au/products/kittenbot-jacdac-kit-a-with-adaptor-for-micro-bit-v2-pakr-a0410" ], services: [ 522667846 ], productIdentifiers: [ 1054684252 ], version: "1.0", status: "stable", shape: "ec30_5x2_lr" }, { id: "kittenbot-ultrasonicsensorv10", name: "Ultrasonic Sensor", company: "KittenBot", description: "The Ultrasonic Distance Measurement module has a detection range of approximately 3.5 meters.", storeLink: [ "https://www.kittenbot.cc/products/jacdac-kit-b-elite-module-suite-redefining-electronic-interfacing" ], services: [ 337275786 ], productIdentifiers: [ 983895657 ], version: "1.0", status: "stable", shape: "ec30_5x2_lr" }, { id: "kitronik-accessbitv11", name: "ACCESS:bit", company: "Kitronik", description: "The ACCESS:bit is a bolt-on/clip-on board for the BBC microbit that simulates an access barrier.", makeCodeRepo: [ { target: "microbit", name: "kitronik-accessbitv11", slug: "pelikhan/pxt-kitronik-accessbit/jacdac" } ], connector: "noConnector", link: "https://kitronik.co.uk/products/5646-accessbit-for-bbc-microbit", storeLink: [ "https://kitronik.co.uk/products/5646-accessbit-for-bbc-microbit" ], services: [ 318542083 ], productIdentifiers: [ 1053651806 ], version: "1.1", status: "stable" }, { id: "kitronik-airqualityboardv10", name: "Air Quality Board", company: "Kitronik", description: "The Kitronik Air Quality Board provides a complete air monitoring and reporting solution for the BBC micro:bit . The wealth of onboard sensors and connection points allow you to collect extensive air quality data that can be stored in onboard memory and displayed on the OLED screen or transferred to a computer for analysis.", makeCodeRepo: [ { target: "microbit", name: "kitronik-airqualityboardv10", slug: "pelikhan/pxt-kitronik-air-quality/jacdac/" } ], connector: "edgeIndependent", link: "https://kitronik.co.uk/products/5674-kitronik-air-quality-board-for-bbc-micro-bit", storeLink: [ "https://kitronik.co.uk/products/5674-kitronik-air-quality-board-for-bbc-micro-bit" ], services: [ 337754823, 504462570, 382210232, 379362758, 445323816, 523748714 ], productIdentifiers: [ 854008423 ], version: "1.0", status: "stable" }, { id: "kitronik-gamezip64v12", name: "Game Zip 64", company: "Kitronik", makeCodeRepo: [ { target: "microbit", name: "kitronik-gamezip64v12", slug: "pelikhan/pxt-kitronik-zip-64/jacdac" } ], connector: "noConnector", link: "https://kitronik.co.uk/products/5626-game-zip-64-for-the-bbc-microbit", storeLink: [ "https://kitronik.co.uk/products/5626-game-zip-64-for-the-bbc-microbit" ], services: [ 277836886, 369743088, 406832290 ], productIdentifiers: [ 888103518 ], version: "1.2", status: "stable" }, { id: "kitronik-servolitev10", name: "SERVO:LITE", company: "Kitronik", description: "The Servo:Lite board for the BBC micro:bit is a simple board that allows you to easily connect and control low power servo motors (servo's must be capable of operating at 3.3V) using the BBC micro:bit. It is connected to the micro:bit using five bolts. Connect two servos in standard configuration and it can drive up to 3 servos if the addressable 'ZIP' LEDs aren\u2019t needed.", makeCodeRepo: [ { target: "microbit", name: "kitronik-servolitev10", slug: "pelikhan/pxt-kitronik-servo-lite/jacdac" } ], connector: "noConnector", link: "https://kitronik.co.uk/products/5623-servolite-board-for-move-mini", storeLink: [ "https://kitronik.co.uk/products/5623-servolite-board-for-move-mini" ], services: [ 369743088, 318542083, 318542083 ], productIdentifiers: [ 995306847 ], version: "1.0", status: "stable" }, { id: "kitronik-stopbitv10", name: "STOP:bit", company: "Kitronik", description: "The STOP:bit for the BBC micro:bit is the ultimate upgrade for traffic light/pedestrian crossing projects.", makeCodeRepo: [ { target: "microbit", name: "kitronik-stopbit-jacdac", slug: "pelikhan/pxt-kitronik-stopbit/jacdac" } ], connector: "noConnector", link: "https://kitronik.co.uk/products/5642-stopbit-traffic-light-for-bbc-microbit", storeLink: [ "https://kitronik.co.uk/products/5642-stopbit-traffic-light-for-bbc-microbit" ], services: [ 365137307 ], productIdentifiers: [ 1058995861 ], version: "1.0", status: "stable" }, { id: "keystudio-relaybreakoutboardv10", name: "Relay Breakout Board", company: "Keystudio", description: "Keyestudio relay breakout board for micro:bit has integrated a 4-way 5V relay module, fully compatible with micro:bit development board.\nIt can work only need to insert micro:bit into keyestudio relay shield, then input DC5V voltage on the relay VIN/GND port, pretty simple and convenient.", makeCodeRepo: [ { target: "microbit", name: "keystudio-relaybreakoutboardv10", slug: "pelikhan/keystudio-relay-breakout-jacdac" } ], connector: "noConnector", link: "https://www.keyestudio.com/products/keyestudio-relay-breakout-board-for-bbc-microbit", storeLink: [ "https://www.keyestudio.com/products/keyestudio-relay-breakout-board-for-bbc-microbit" ], services: [ 406840918, 406840918, 406840918, 406840918 ], productIdentifiers: [ 901245225 ], version: "1.0", status: "stable" }, { id: "forward-education-breakoutboardrelaypumpv10", name: "Breakout Board + Relay + Pump", company: "Forward Education", description: "A micro:bit carrier board", makeCodeRepo: [ { target: "microbit", name: "forward-education-breakoutboardrelaypumpv10", slug: "climate-action-kits/pxt-fwd-edu/fwd-breakout" } ], link: "https://forwardedu.com", storeLink: [ "https://forwardedu.com" ], services: [ 406840918, 318542083 ], productIdentifiers: [ 873600795, 943529908 ], tags: [ "adapter", "microbit" ], version: "1.0", status: "stable" }, { id: "forward-education-climateactionkitv10", name: "Climate Action Kit", company: "Forward Education", description: "Purpose-driven STEM education for a brighter future. \xA0Explore climate action with our STEM kit and online lessons.", makeCodeRepo: [ { target: "microbit", name: "pxt-fwd-edu", slug: "climate-action-kits/pxt-fwd-edu/" } ], link: "https://forwardedu.com", storeLink: [ "https://forwardedu.com" ], tags: [ "kit", "microbit" ], version: "1.0", status: "stable", devices: [ "forward-education-linesensorv10", "forward-education-breakoutboardrelaypumpv10", "forward-education-ledlightsv10", "forward-education-touchsensorv10", "forward-education-dialbuttonv10", "forward-education-sonarsensorv10", "forward-education-solarsensorv10", "forward-education-moisturesensorv10" ], relatedDevices: [ "microbit-educational-foundation-microbitv2" ], requiredDevices: [ "forward-education-breakoutboardrelaypumpv10", "microbit-educational-foundation-microbitv2" ] }, { id: "forward-education-dialbuttonv10", name: "Dial Button", company: "Forward Education", description: "Dial Button is a rotary encoder and button compatible with the jacdac framework and part of the Climate Action Kit by Forward Education\n", makeCodeRepo: [ { target: "microbit", name: "forward-education-dialbuttonv10", slug: "climate-action-kits/pxt-fwd-edu/fwd-dial" } ], link: "https://forwardedu.com", storeLink: [ "https://forwardedu.com" ], services: [ 343122531, 284830153 ], productIdentifiers: [ 932148851 ], version: "1.0", status: "stable" }, { id: "forward-education-ledlightsv10", name: "LED Lights", company: "Forward Education", description: "LED Lights is a ws2812 LED ring compatible with the jacdac framework and part of the Climate Action Kit by Forward Education\n", makeCodeRepo: [ { target: "microbit", name: "fwd-edu-led", slug: "climate-action-kits/pxt-fwd-edu/fwd-led" } ], link: "https://forwardedu.com", storeLink: [ "https://forwardedu.com" ], services: [ 369743088 ], productIdentifiers: [ 1054009247 ], version: "1.0", status: "stable" }, { id: "forward-education-linesensorv10", name: "Line Sensor", company: "Forward Education", description: "Line Sensor is a multi-reflected light sensor compatible with the jacdac framework and part of the Climate Action Kit by Forward Education\n", makeCodeRepo: [ { target: "microbit", name: "fwd-edu-line", slug: "climate-action-kits/pxt-fwd-edu/fwd-line" } ], link: "https://forwardedu.com", storeLink: [ "https://forwardedu.com" ], services: [ 309087410, 309087410, 309087410 ], productIdentifiers: [ 1000568338 ], version: "1.0", status: "stable" }, { id: "forward-education-moisturesensorv10", name: "Moisture Sensor", company: "Forward Education", description: "Moisture Sensor is a capacitive soil moisture sensor compatible with the jacdac framework and part of the Climate Action Kit by Forward Education\n", makeCodeRepo: [ { target: "microbit", name: "fwd-edu-moisture", slug: "climate-action-kits/pxt-fwd-edu/fwd-moisture" } ], link: "https://forwardedu.com", storeLink: [ "https://forwardedu.com" ], services: [ 491430835 ], productIdentifiers: [ 870361737 ], version: "1.0", status: "stable" }, { id: "forward-education-solarsensorv10", name: "Solar Sensor", company: "Forward Education", description: "Solar Sensor is a photoresistor sensor compatible with the jacdac framework and part of the Climate Action Kit by Forward Education", makeCodeRepo: [ { target: "microbit", name: "fwd-edu-solar", slug: "climate-action-kits/pxt-fwd-edu/fwd-solar" } ], link: "https://forwardedu.com", storeLink: [ "https://forwardedu.com" ], services: [ 400333340 ], productIdentifiers: [ 1005257879 ], version: "1.0", status: "stable" }, { id: "forward-education-sonarsensorv10", name: "Sonar Sensor", company: "Forward Education", description: "Sonar Sensor is an ultrasonic/radar sensor compatible with the jacdac framework and part of the Climate Action Kit by Forward Education\n", makeCodeRepo: [ { target: "microbit", name: "fwd-edu-sonar", slug: "climate-action-kits/pxt-fwd-edu/fwd-sonar" } ], link: "https://forwardedu.com", storeLink: [ "https://forwardedu.com" ], services: [ 337275786 ], productIdentifiers: [ 926591985 ], version: "1.0", status: "stable" }, { id: "forward-education-touchsensorv10", name: "Touch Sensor", company: "Forward Education", description: "Touch Sensor is a capacitive touch sensor compatible with the jacdac framework and part of the Climate Action Kit by Forward Education\n", makeCodeRepo: [ { target: "microbit", name: "fwd-edu-touch", slug: "climate-action-kits/pxt-fwd-edu/fwd-touch" } ], link: "https://forwardedu.com", storeLink: [ "https://forwardedu.com" ], services: [ 343122531 ], productIdentifiers: [ 1064434129 ], version: "1.0", status: "stable" }, { id: "espressif-esp32c3rustdevkitv12a", name: "ESP32-C3-RUST-DevKit", company: "Espressif", description: "This board is based on the ESP32-C3, and includes sensors, LEDs, buttons, a battery charger, and USB type-C connector.", repo: "https://github.com/microsoft/devicescript-esp32", firmwareSource: "https://github.com/microsoft/devicescript-esp32/blob/main/boards/esp32c3/esp32c3_rust_devkit.board.json", hardwareDesign: "https://github.com/esp-rs/esp-rust-board", connector: "noConnector", link: "https://github.com/esp-rs/esp-rust-board", storeLink: [ "https://github.com/esp-rs/esp-rust-board" ], services: [ 413852154, 342028028, 341864092, 337754823, 382210232, 471386691, 343122531 ], productIdentifiers: [ 871537753 ], version: "1.2a", status: "stable" }, { id: "espressif-esp32devkitcdevicescriptv40", name: "ESP32-DevKitC DeviceScript", company: "Espressif", description: "This will also work with NodeMCU etc.", firmwareSource: "https://github.com/microsoft/devicescript-esp32", connector: "noConnector", link: "https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html", storeLink: [ "https://www.espressif.com/en/products/devkits/esp32-devkitc" ], services: [ 341864092, 342028028, 413852154, 288680491 ], productIdentifiers: [ 1011907077 ], transport: { type: "serial", vendorId: 4292 }, firmwares: [ { name: "DeviceScript + Cloud Connector", url: "https://github.com/microsoft/devicescript-esp32/releases/latest", productIdentifier: 1011907077 } ], version: "4.0", bootloader: { sequence: "boot-power", firmwareFormat: "bin", firmwareUploader: "https://adafruit.github.io/Adafruit_WebSerial_ESPTool/" }, status: "stable" }, { id: "calliope-ggmbh-calliopeminiv3v30", name: "Calliope mini V3", company: "Calliope gGmbH", description: "Calliope mini is an easy-to-program microcontroller that has quite a few internal sensors and, in addition to a pin header and two Grove connectors it also features two JacDac connectors to connect sensors and actuators directly to the board.", connector: "edgeLowCurrentProvider", link: "https://calliope.cc", storeLink: [ "https://shop.calliope.cc" ], productIdentifiers: [ 860332754 ], transport: { type: "usb", requestDescription: "Calliope mini V3 CMSIS-DAP" }, version: "3.0", status: "stable" }, { id: "calliope-ggmbh-calliopescd40v10", name: "Calliope SCD40", company: "Calliope gGmbH", description: "The SCD40 is a miniature CO2 sensor from Sensirion. This sensor builds on the photoacoustic NDIR sensing principle. It provides On-chip signal \ncompensation with the build-in humidity and temperature sensor.", connector: "grove", link: "https://shop.calliope.cc/en/products/scd40-calliope-mini-co2-sensor", storeLink: [ "https://shop.calliope.cc/en/products/scd40-calliope-mini-co2-sensor" ], services: [ 379362758, 382210232, 337754823 ], productIdentifiers: [ 895527115 ], version: "1.0", status: "stable" }, { id: "adafruit-qtpyesp32c3wifidevboardv10", name: "QT Py ESP32-C3 WiFi Dev Board", company: "Adafruit", description: "The ESP32-C3 integrates a rich set of peripherals, ranging from UART, I2C, I2S, remote control peripheral, LED PWM controller, general DMA controller, TWAI controller, USB Serial/JTAG controller, temperature sensor, and ADC. It also includes SPI, Dual SPI, and Quad SPI interfaces.", firmwareSource: "https://github.com/microsoft/devicescript-esp32", connector: "qwiic", link: "https://www.adafruit.com/product/5405", storeLink: [ "https://www.adafruit.com/product/5405" ], services: [ 341864092, 342028028, 413852154, 288680491 ], productIdentifiers: [ 866451573, 915657739 ], firmwares: [ { name: "DeviceScript + Cloud Connector", url: "https://github.com/microsoft/devicescript-esp32/releases/latest", productIdentifier: 915657739 } ], version: "1.0", bootloader: { sequence: "boot-power", firmwareFormat: "bin", firmwareUploader: "https://adafruit.github.io/Adafruit_WebSerial_ESPTool/" }, status: "stable" }, { id: "01space-esp32c3fh4rgbv10", name: "ESP32-C3FH4-RGB", company: "01Space", description: "Small ESP32-C3 board with 5x5 LED color matrix", connector: "noConnector", link: "https://github.com/01Space/ESP32-C3FH4-RGB", storeLink: [ "https://usa.banggood.com/ESP32-C3-Development-Board-RISC-V-WiFi-Bluetooth-IoT-Development-Board-Compatible-with-Python-p-1914005.html?imageAb=2&akmClientCountry=America&a=1694552315.7453&akmClientCountry=America&cur_warehouse=CN" ], services: [ 288680491, 369743088, 343122531 ], productIdentifiers: [ 982550620 ], version: "1.0", status: "stable" }, { id: "seeed-studio-xiaoesp32c3withmsr218base218v46", name: "XIAO ESP32C3 with MSR218 base", company: "Seeed Studio", description: "XIAO ESP32C3 + Prototype Carrier board for Xiao, Jacdac, Grove, Qwiic, Stemma QT, Analog.", repo: "https://github.com/microsoft/devicescript-esp32", firmwareSource: "https://github.com/microsoft/devicescript-esp32/blob/main/boards/esp32c3/seeed_xiao_esp32c3.board.json", connector: "edgeHighCurrentProvider", services: [ 282614377, 413852154, 342028028, 341864092, 471386691 ], productIdentifiers: [ 917915687 ], version: "4.6", designIdentifier: "218", shape: "ec30_4x3_l2" }, { id: "milador-jmpressuresensorv10", name: "JM Pressure Sensor v1.0", company: "Milador", description: "JM Pressure Sensor v1.0", repo: "https://github.com/milador/jacdac-milador-modules", firmwareSource: "https://github.com/milador/jacdac-milador-modules", hardwareDesign: "https://github.com/milador/Jacdac-Pressure-Sensor/tree/develop/Hardware/PCB", link: "https://github.com/milador/Jacdac-Pressure-Sensor/", services: [ 504462570, 337754823 ], productIdentifiers: [ 962878031 ] }, { id: "milador-pressuresensorv11", name: "Pressure Sensor", company: "Milador", description: "A Jacdac Pressure Sensor Module based on LPS33HW Absolute digital Pressure Sensor.", repo: "https://github.com/milador/jacdac-milador-modules", hardwareDesign: "https://github.com/milador/Jacdac-Pressure-Sensor/tree/develop/Hardware/PCB", link: "https://github.com/milador/Jacdac-Pressure-Sensor", services: [ 504462570, 337754823 ], productIdentifiers: [ 867414479 ], version: "1.1", shape: "ec30_2x2_lr" }, { id: "microsoft-research-cableextender38v10", name: "CableExtender", company: "Microsoft Research", description: "Cable extender", connector: "edgePassive", productIdentifiers: [ 928595161 ], version: "1.0", designIdentifier: "38", status: "experimental" }, { id: "microsoft-research-cableextender38v11", name: "CableExtender", company: "Microsoft Research", description: "Cable extender", hardwareDesign: "https://github.com/microsoft/jacdac-ddk/tree/main/electronics/altium/hub-designs/JacdacCableExtender%2038-1.1", connector: "edgePassive", productIdentifiers: [ 943041789 ], version: "1.1", designIdentifier: "38" }, { id: "microsoft-research-co2209v43", name: "CO2", company: "Microsoft Research", description: "Measures real CO2 concentration using SCD40 sensor (plus temperature and humidity)", repo: "https://github.com/microsoft/jacdac-msr-modules", services: [ 337754823, 382210232, 379362758 ], productIdentifiers: [ 1008301288 ], version: "4.3", designIdentifier: "209", shape: "ec30_2x2_lr" }, { id: "microsoft-research-developerrgb117v10", name: "Developer RGB", company: "Microsoft Research", description: "Developer module with 10-pin header and RGB LED", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/developer-rgb-117/profile/developer-rgb-117-v1.0.c", hardwareDesign: "https://github.com/microsoft/jacdac-ddk/tree/main/electronics/altium/module-designs/JacdacDevRgbEc30%20117-1.0", services: [ 506480888 ], productIdentifiers: [ 851267569 ], version: "1.0", designIdentifier: "117", shape: "ec30_2x2_lr" }, { id: "microsoft-research-devicescriptsimulatorv10", name: "DeviceScript Simulator", company: "Microsoft Research", description: "A virtual DeviceScript Manager simulator.", firmwareSource: "https://github.com/microsoft/devicescript", connector: "noConnector", services: [ 358308672 ], productIdentifiers: [ 1072018543, 288680491 ], version: "1.0" }, { id: "microsoft-research-hub114v10", name: "Hub", company: "Microsoft Research", description: "Passive hub with 6 ports", hardwareDesign: "https://github.com/microsoft/jacdac-ddk/tree/main/electronics/altium/hub-designs/JacdacEc30Hub10x40%20114-1.0", connector: "edgePassive", version: "1.0", designIdentifier: "114", shape: "ec30_2x4_lr" }, { id: "microsoft-research-jacdacjoystick440344v03", name: "JacdacJoystick 44-0.3", company: "Microsoft Research", firmwareSource: "https://github.com/microsoft/jacdac-padauk/tree/main/jm-joystick-44-0.2", link: "https://github.com/microsoft/jacdac-padauk", services: [ 277836886 ], productIdentifiers: [ 832285283 ], version: "0.3", designIdentifier: "44", status: "experimental" }, { id: "microsoft-research-jacdacmicrobitshieldlp29v03", name: "JacDacMicroBitShieldLP", company: "Microsoft Research", connector: "edgeLowCurrentProvider", productIdentifiers: [ 1009620586 ], version: "0.3", designIdentifier: "29", status: "experimental" }, { id: "microsoft-research-jacdacmotiondetection54v01", name: "JacdacMotionDetection ", company: "Microsoft Research", link: "https://github.com/microsoft/jacdac-padauk", services: [ 293185353 ], productIdentifiers: [ 1030407429 ], version: "0.1", designIdentifier: "54", status: "experimental" }, { id: "microsoft-research-jacdacpinheaders45v02", name: "JacdacPinHeaders ", company: "Microsoft Research", productIdentifiers: [ 970939382 ], version: "0.2", designIdentifier: "45", status: "experimental" }, { id: "microsoft-research-jacdactouchtest35v10", name: "JacdacTouchTest", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", services: [ 677752265 ], productIdentifiers: [ 933677864 ], version: "1.0", designIdentifier: "35", status: "experimental" }, { id: "microsoft-research-jacdactouchtestelectrode36v10", name: "JacdacTouchTestElectrode", company: "Microsoft Research", services: [ 677752265 ], productIdentifiers: [ 1026187559 ], version: "1.0", designIdentifier: "36", status: "experimental" }, { id: "microsoft-research-jm3slider85v40", name: "JM-3-Slider", company: "Microsoft Research", description: "Triple slider", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v4.0/profile/slider-85.c", services: [ 522667846, 522667846, 522667846 ], productIdentifiers: [ 905702802 ], version: "4.0", designIdentifier: "85", shape: "ec30_6x3_lr" }, { id: "microsoft-research-jmaccelerometer30v10", name: "JM Accelerometer", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-accelerometer-30-1.0/profile/accelerometer.c", services: [ 521405449 ], productIdentifiers: [ 952491663 ], version: "1.0", designIdentifier: "30" }, { id: "microsoft-research-jmaccessswitchinput34v13", name: "JM Access Switch Input", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-access-switch-input-34-1.3/profile/xac.c", services: [ 343122531, 522667846, 277836886 ], productIdentifiers: [ 964964313 ], version: "1.3", designIdentifier: "34" }, { id: "microsoft-research-jmaccessswitchoutputv11", name: "JM Access Switch Output", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-access-switch-output-1.1/profile/relay.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 406840918 ], productIdentifiers: [ 942325999 ], version: "1.1" }, { id: "microsoft-research-jmambientlight55v01", name: "JM Ambient Light", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-padauk/tree/main/jm-ambient-lightsensor-55-0.1", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 400333340 ], productIdentifiers: [ 896864987 ], version: "0.1", designIdentifier: "55" }, { id: "microsoft-research-jmanalogjoystick44v02", name: "JM Analog Joystick", company: "Microsoft Research", firmwareSource: "https://github.com/microsoft/jacdac-padauk/tree/main/jm-joystick-44-0.2", link: "https://github.com/microsoft/jacdac-padauk", services: [ 277836886 ], productIdentifiers: [ 976429228 ], version: "0.2", designIdentifier: "44" }, { id: "microsoft-research-jmbase86v41", name: "JM Base", company: "Microsoft Research", description: "A breadboard like PCB to screw mount Jacdac modules.", connector: "edgePassive", tags: [ "ec30", "hub" ], version: "4.1", designIdentifier: "86" }, { id: "microsoft-research-jmbrainesp3248v03", name: "JM Brain ESP32", company: "Microsoft Research", connector: "edgeHighCurrentProvider", services: [ 342028028, 341864092 ], productIdentifiers: [ 917230668, 983264687, 1067560617, 1038170507, 816040071 ], transport: { type: "serial", vendorId: 12346 }, firmwares: [ { name: "Azure IoT Hub Uploader", url: "https://github.com/microsoft/pxt-jacdac/releases/latest/download/uploader-esp32s2.uf2", productIdentifier: 1067560617 }, { name: "DeviceScript Brain + Azure IoT Hub Cloud Adapter", url: "https://github.com/microsoft/devicescript-esp32/releases/latest/download/devicescript-esp32s2.uf2", productIdentifier: 1038170507 }, { name: "Azure IoT Hub Cloud Adapter", url: "https://github.com/microsoft/devicescript-esp32/releases/latest/download/devicescript-esp32s2-nojacs.uf2", productIdentifier: 816040071 } ], version: "0.3", designIdentifier: "48", bootloader: { sequence: "reset-boot", driveName: "IOT-BOOT", firmwareFormat: "uf2", ledAnimation: "blue-glow" } }, { id: "microsoft-research-jmbrainf441v02", name: "JM Brain F4", company: "Microsoft Research", connector: "edgeHighCurrentProvider", link: "https://github.com/microsoft/pxt-jacdac", services: [ 414210922, 411425820 ], productIdentifiers: [ 1003209864, 970267564, 819577746, 1009312972 ], transport: { type: "usb" }, firmwares: [ { name: "HID Keyboard + Mouse", url: "https://github.com/microsoft/pxt-jacdac/releases/latest/download/hid-servers-f4.uf2", productIdentifier: 819577746 }, { name: "HID Joystick", url: "https://github.com/microsoft/pxt-jacdac/releases/latest/download/hid-joystick-f4.uf2", productIdentifier: 1009312972 } ], version: "0.2", designIdentifier: "41", bootloader: { sequence: "reset", driveName: "JACDACF4", firmwareFormat: "uf2", ledAnimation: "blue-glow" } }, { id: "microsoft-research-jmbrainrp204059v01", name: "JM Brain RP2040", company: "Microsoft Research", connector: "edgeHighCurrentProvider", link: "https://github.com/microsoft/pxt-jacdac", services: [ 414210922, 411425820 ], productIdentifiers: [ 884301483, 999933064, 883764657, 983850333 ], transport: { type: "usb" }, tags: [ "adapter" ], firmwares: [ { name: "HID Keyboard + Mouse", url: "https://github.com/microsoft/pxt-jacdac/releases/latest/download/hid-servers-rp2040.uf2", productIdentifier: 883764657 }, { name: "HID Joystick", url: "https://github.com/microsoft/pxt-jacdac/releases/latest/download/hid-joystick-rp2040.uf2", productIdentifier: 983850333 } ], version: "0.1", designIdentifier: "59", bootloader: { sequence: "reset-boot", driveName: "RPI-RP2", firmwareFormat: "uf2" } }, { id: "microsoft-research-jmbutton10v13", name: "JM Button", company: "Microsoft Research", link: "https://github.com/microsoft/jacdac-padauk", services: [ 343122531 ], productIdentifiers: [ 896566497 ], version: "1.3", designIdentifier: "10" }, { id: "microsoft-research-jmbutton40v02", name: "JM Button", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-button-40-0.2/profile/button.c", services: [ 343122531 ], productIdentifiers: [ 946173966 ], version: "0.2", designIdentifier: "40" }, { id: "microsoft-research-jmbuttonterminal62v01", name: "JM Button Terminal", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 343122531 ], productIdentifiers: [ 1047530059 ], version: "0.1", designIdentifier: "62" }, { id: "microsoft-research-jmbuzzer89", name: "JM-Buzzer", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v4.0/profile/buzzer-89.c", services: [ 458731991 ], productIdentifiers: [ 1073013851 ], designIdentifier: "89", shape: "ec30_2x2_lr" }, { id: "microsoft-research-jmcapacitivesoilmoisturev33", name: "JM Capacitive Soil Moisture", company: "Microsoft Research", description: "JM Capacitive Soil Moisture v3.3", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.3/profile/soilmoisture.c", services: [ 491430835 ], productIdentifiers: [ 959462330 ], version: "3.3" }, { id: "microsoft-research-jmclickairquality4v32", name: "JM-Click Airquality4", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.2/profile/airquality4click.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 379362758, 312849815 ], productIdentifiers: [ 878106432 ], version: "3.2" }, { id: "microsoft-research-jmclickcolorv32", name: "JM-Click Color", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.2/profile/colorclick.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 372299111 ], productIdentifiers: [ 1020991645 ], version: "3.2" }, { id: "microsoft-research-jmco2tvoctemphumsgp30sht3066v37", name: "JM CO2/TVOC/Temp/Hum SGP30+SHT30", company: "Microsoft Research", description: "JM CO2/TVOC/Temp/Hum SGP30+SHT30 66 v3.7", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.6/profile/co2.c", services: [ 379362758, 312849815, 337754823, 382210232 ], productIdentifiers: [ 912293656 ], version: "3.7", designIdentifier: "66" }, { id: "microsoft-research-jmdualkey69v37", name: "JM Dual Key", company: "Microsoft Research", description: "JM 2Key 69 v3.7", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.6/profile/key2.c", services: [ 343122531, 343122531 ], productIdentifiers: [ 958599316 ], version: "3.7", designIdentifier: "69" }, { id: "microsoft-research-jmenvironment204v42", name: "JM-Environment", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", services: [ 337754823, 382210232, 379362758, 312849815 ], productIdentifiers: [ 842012177 ], version: "4.2", designIdentifier: "204", shape: "ec30_2x2_l" }, { id: "microsoft-research-jmflexv10", name: "JM Flex", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-flex-sensor-1.0/profile/flex.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 524797638 ], productIdentifiers: [ 840841542 ], version: "1.0" }, { id: "microsoft-research-jmgamepadv20", name: "JM GamePad", company: "Microsoft Research", description: "Lets you convert a plastic d-pad controller, so that it can be connected to a Jacdac network.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0/profile/dpad.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 501915758 ], productIdentifiers: [ 919754666 ], version: "2.0" }, { id: "microsoft-research-jmhallmagneticswitch81v40", name: "JM-Hall (magnetic switch)", company: "Microsoft Research", description: "JM-Hall (magnetic switch) 81 v4.0", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v4.0/profile/hall-81.c", services: [ 450008066 ], productIdentifiers: [ 920333141 ], version: "4.0", designIdentifier: "81", shape: "ec30_2x2_lr" }, { id: "microsoft-research-jmhapticmach101v10", name: "JM Haptic MACH-1.0", company: "Microsoft Research", description: "JM Haptic MACH-1.0", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-haptic-1.0/profile/haptic.c", services: [ 406832290 ], productIdentifiers: [ 1022649261 ], version: "1.0", designIdentifier: "1" }, { id: "microsoft-research-jmhub39v03", name: "JM Hub", company: "Microsoft Research", productIdentifiers: [ 917230668 ], version: "0.3", designIdentifier: "39", status: "experimental" }, { id: "microsoft-research-jmjacscriptstarbrainv33", name: "JM Jacscript Star-Brain", company: "Microsoft Research", description: "A Jacscript brain", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/star-brain/profile/devicescript.c", services: [ 288680491 ], productIdentifiers: [ 1025500463 ], transport: { type: "serial", vendorId: 6790 }, version: "3.3" }, { id: "microsoft-research-jmkeyboardkey46v12", name: "JM Keyboard Key", company: "Microsoft Research", link: "https://github.com/microsoft/jacdac-padauk", services: [ 343122531 ], productIdentifiers: [ 876567534 ], version: "1.2", designIdentifier: "46" }, { id: "microsoft-research-jmkeyv3768v37", name: "JM Key v3.7", company: "Microsoft Research", description: "JM Key 68 v3.7", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.6/profile/key.c", services: [ 343122531 ], productIdentifiers: [ 948311172 ], version: "3.7", designIdentifier: "68" }, { id: "microsoft-research-jmmatrix87v40", name: "JM-Matrix", company: "Microsoft Research", description: "5x5 LED dot matrix", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v4.0matrix/profile/matrix-87.c", services: [ 286070091 ], productIdentifiers: [ 896568761 ], version: "4.0", designIdentifier: "87", shape: "ec30_2x2_lr" }, { id: "microsoft-research-jmmicrobitshieldlp29v05", name: "JM MicroBit Shield LP", company: "Microsoft Research", hardwareDesign: "https://github.com/microsoft/jacdac-ddk/tree/main/electronics/deprecated_form_factor/altium_deprecated/reference-designs/JacdacMicroBitShieldLP%2029-0.6", connector: "edgeLowCurrentProvider", productIdentifiers: [ 974031363 ], tags: [ "adapter", "microbit" ], version: "0.5", designIdentifier: "29" }, { id: "microsoft-research-jmmoduletester91v01", name: "JM Module Tester", company: "Microsoft Research", description: "A module that controls the Jacdac bus voltage and measures the bus current consumption.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-module-tester-91-0.1/board.h", services: [ 524302175, 420661422, 372485145, 406840918, 506480888 ], productIdentifiers: [ 1031963291 ], version: "0.1", designIdentifier: "91", shape: "ec30_5x2_lr" }, { id: "microsoft-research-jmmotionpirv3874v38", name: "JM Motion PIR v3.8", company: "Microsoft Research", description: "JM Motion PIR 74 v3.8", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.3/profile/pir.c", link: "https://aka.ms/aaaaad", services: [ 293185353 ], productIdentifiers: [ 1060186023 ], version: "3.8", designIdentifier: "74", shape: "ec30_2x2_lr" }, { id: "microsoft-research-jmpinheader45v02", name: "JM Pin Header", company: "Microsoft Research", description: "A regulated passive adapter from Jacdac to pin headers.", link: "https://github.com/microsoft/jacdac-msr-modules", productIdentifiers: [ 994328823 ], version: "0.2", designIdentifier: "45" }, { id: "microsoft-research-jmpressurev3872v38", name: "JM Pressure v3.8", company: "Microsoft Research", description: "JM Pressure MPL3115A2 72 v3.8", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.6/profile/barometer2.c", link: "https://aka.ms/aaaabb", services: [ 504462570, 337754823 ], productIdentifiers: [ 869351851 ], version: "3.8", designIdentifier: "72", shape: "ec30_2x2_lr" }, { id: "microsoft-research-jmrelaymach01v10", name: "JM Relay MACH-0.1", company: "Microsoft Research", description: "JM Relay MACH-0.1", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-relay-1.0/profile/relay.c", services: [ 406840918 ], productIdentifiers: [ 900132124 ], version: "1.0" }, { id: "microsoft-research-jmrgb883v40", name: "JM RGB-8", company: "Microsoft Research", description: "Circular 8 programmable LED display.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v4.0/profile/rgb-83.c", services: [ 369743088 ], productIdentifiers: [ 1050545633 ], version: "4.0", designIdentifier: "83", shape: "ec30_3x3_lr" }, { id: "microsoft-research-jmrgbledbar58v01", name: "JM RGB LED Bar", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-rgb-led-bar-58-0.1/profile/npx.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 309264608 ], productIdentifiers: [ 1046525691 ], version: "0.1", designIdentifier: "58" }, { id: "microsoft-research-jmrgbledgeneric60v01", name: "JM RGB LED Generic", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-rgb-led-generic-60-0.1/profile/npx.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 309264608 ], productIdentifiers: [ 967723905 ], version: "0.1", designIdentifier: "60" }, { id: "microsoft-research-jmrgbledring37v21", name: "JM RGB LED Ring", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-rgb-led-ring-37-2.1/profile/npx.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 309264608 ], productIdentifiers: [ 807926135 ], version: "2.1", designIdentifier: "37" }, { id: "microsoft-research-jmrgbring67v37", name: "JM RGB-Ring", company: "Microsoft Research", description: "JM RGB-Ring 67 v3.7", repo: "https://github.com/microsoft/jacdac-msr-modules", services: [ 309264608 ], productIdentifiers: [ 946180442 ], version: "3.7", designIdentifier: "67" }, { id: "microsoft-research-jmrotarybtn82v40", name: "JM Rotary + Btn", company: "Microsoft Research", description: "Rotary encoder with button in the standard shape.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v4.0rot/profile/rotary-82.c", services: [ 284830153, 343122531 ], productIdentifiers: [ 1045398971 ], version: "4.0", designIdentifier: "82", shape: "ec30_2x2_l" }, { id: "microsoft-research-jmrotarycontrolbuttonv11", name: "JM Rotary Control + Button", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-rotary-control-26-1.1/profile/rotary_ctrl.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 284830153, 343122531 ], productIdentifiers: [ 829647613 ], version: "1.1" }, { id: "microsoft-research-jmsht30temperaturehumidity64v36", name: "JM SHT30 Temperature/Humidity", company: "Microsoft Research", description: "JM SHT30 64 v3.6", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.6/profile/env30.c", services: [ 337754823, 382210232 ], productIdentifiers: [ 819953075 ], version: "3.6", designIdentifier: "64" }, { id: "microsoft-research-jmsinglergbled42v01", name: "JM Single RGB LED", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 506480888 ], productIdentifiers: [ 917828732 ], version: "0.1", designIdentifier: "42" }, { id: "microsoft-research-jmslider49v11", name: "JM Slider", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-slider-49-1.1/profile/slider.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 522667846 ], productIdentifiers: [ 966423091 ], version: "1.1", designIdentifier: "49" }, { id: "microsoft-research-jmsoiltemperatureds18b20v33", name: "JM Soil Temperature DS18B20", company: "Microsoft Research", description: "JM Soil Temperature DS18B20 v3.3", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.3one/profile/soiltemp.c", services: [ 337754823 ], productIdentifiers: [ 1039295899 ], version: "3.3" }, { id: "microsoft-research-jmspeechsynthesis61v33", name: "JM Speech Synthesis", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.3/profile/ttsclick.c", services: [ 302307733 ], productIdentifiers: [ 934541191 ], version: "3.3", designIdentifier: "61" }, { id: "microsoft-research-jmspibridgev37", name: "JM SPI bridge", company: "Microsoft Research", description: "JM SPI bridge v3.7", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-spi-bridge-v3.7/profile/bridge.c", services: [ 535147631, 530893146 ], productIdentifiers: [ 882232420 ], tags: [ "adapter", "pi" ], version: "3.7" }, { id: "microsoft-research-jmtemperaturehumidity18v11", name: "JM Temperature + Humidity", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-temp-humidity-18-1.1/profile/env3.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 337754823, 382210232 ], productIdentifiers: [ 827772887 ], version: "1.1", designIdentifier: "18" }, { id: "microsoft-research-jmtemperaturehumidity202v41", name: "JM-Temperature/Humidity", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v4.0/profile/temphum-202.c", services: [ 337754823, 382210232 ], productIdentifiers: [ 891386792 ], version: "4.1", designIdentifier: "202", shape: "ec30_2x2_lr" }, { id: "microsoft-research-jmthermocouplemax667573v3873v38", name: "JM Thermocouple MAX6675 73 v3.8", company: "Microsoft Research", description: "JM Thermocouple MAX6675 v3.8", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-c/blob/main/drivers/max6675.c", link: "https://aka.ms/aaaabc", services: [ 337754823 ], productIdentifiers: [ 1047456763 ], version: "3.8", designIdentifier: "73" }, { id: "microsoft-research-jmuviilluminance65v3665v36", name: "JM UVI/Illuminance 65 v3.6", company: "Microsoft Research", description: "JM UVI/Illuminance 65 v3.6", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v3.6/profile/uv.c", services: [ 510577394, 527306128 ], productIdentifiers: [ 1021617002 ], version: "3.6", designIdentifier: "65" }, { id: "microsoft-research-makeaccessible2021kitv10", name: "MakeAccessible 2021 Kit", company: "Microsoft Research", description: "A kit for the Microsoft MakeAccessible hackathon.", link: "https://www.microsoft.com/en-us/research/project/project-makeaccessible/", tags: [ "kit" ], version: "1.0", devices: [ "microbit-educational-foundation-microbitv2", "microsoft-research-jmaccelerometer30v10", "microsoft-research-jmaccessswitchinput34v13", "microsoft-research-jmaccessswitchoutputv11", "microsoft-research-jmambientlight55v01", "microsoft-research-jmanalogjoystick44v02", "microsoft-research-jmbrainf441v02", "microsoft-research-jmbrainrp204059v01", "microsoft-research-jmbutton10v13", "microsoft-research-jmbutton40v02", "microsoft-research-jmbuttonterminal62v01", "microsoft-research-jmclickairquality4v32", "microsoft-research-jmclickcolorv32", "microsoft-research-jmflexv10", "microsoft-research-jmkeyboardkey46v10", "microsoft-research-jmkeyboardkey46v11", "microsoft-research-jmrgbledbar58v01", "microsoft-research-jmrgbledgeneric60v01", "microsoft-research-jmrgbledring37v20", "microsoft-research-jmrgbledring37v21", "microsoft-research-jmrotarycontrolbuttonv10", "microsoft-research-jmrotarycontrolbuttonv11", "microsoft-research-jmsinglergbled42v01", "microsoft-research-jmslider49v11", "microsoft-research-jmtemperaturehumidity18v11" ] }, { id: "microsoft-research-mikrobuscarrierboard53v01", name: "MikrobusCarrierBoard", company: "Microsoft Research", productIdentifiers: [ 961789360 ], version: "0.1", designIdentifier: "53", status: "experimental" }, { id: "microsoft-research-motionpir210v43", name: "Motion (PIR)", company: "Microsoft Research", description: "Detects motion using infra-red", repo: "https://github.com/microsoft/jacdac-msr-modules", services: [ 293185353 ], productIdentifiers: [ 927235767 ], version: "4.3", designIdentifier: "210", shape: "ec30_2x2_lr" }, { id: "microsoft-research-pressure211v43", name: "Pressure", company: "Microsoft Research", description: "Measures atmospheric pressure", repo: "https://github.com/microsoft/jacdac-msr-modules", services: [ 337754823, 504462570 ], productIdentifiers: [ 816579443 ], version: "4.3", designIdentifier: "211", shape: "ec30_2x2_lr" }, { id: "microsoft-research-rp2040devicescript59v01", name: "RP2040 DeviceScript", company: "Microsoft Research", description: "RP2040 brain running DeviceScript and related services", firmwareSource: "https://github.com/microsoft/devicescript-pico", connector: "edgeHighCurrentProvider", link: "https://github.com/microsoft/devicescript-pico", services: [ 530893146, 414210922, 411425820, 437330261 ], productIdentifiers: [ 900102307 ], transport: { type: "serial", vendorId: 11914 }, version: "0.1", designIdentifier: "59" }, { id: "microsoft-research-tact115v11", name: "Tact", company: "Microsoft Research", description: "Tact style push switch.", hardwareDesign: "https://github.com/microsoft/jacdac-ddk/tree/main/electronics/altium/module-designs/JacdacTactEc30%20115-1.1", services: [ 343122531 ], productIdentifiers: [ 1059364193 ], version: "1.1", designIdentifier: "115", shape: "ec30_1x2_l" }, { id: "microsoft-research-temprh116v10", name: "Temp & RH", company: "Microsoft Research", description: "Temperature and relative humidity sensor module.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/temp-rh-116/profile/temp-rh-116-v1.0.c", hardwareDesign: "https://github.com/microsoft/jacdac-ddk/tree/main/electronics/altium/module-designs/JacdacTempRhEc30%20116-1.0", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 337754823, 382210232 ], productIdentifiers: [ 819808106 ], version: "1.0", designIdentifier: "116", shape: "ec30_2x2_l" }, { id: "microsoft-research-temprh116v11", name: "Temp & RH", company: "Microsoft Research", description: "Temperature and relative humidity sensor module.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/temp-rh-116/profile/temp-rh-116-v1.1.c", hardwareDesign: "https://github.com/microsoft/jacdac-ddk/tree/main/electronics/altium/module-designs/JacdacTempRhEc30%20116-1.1", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 337754823, 382210232 ], productIdentifiers: [ 1066966718 ], version: "1.1", designIdentifier: "116", shape: "ec30_2x2_l" }, { id: "microsoft-research-uviilluminance205v42", name: "UVI/Illuminance", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", services: [ 510577394, 527306128 ], productIdentifiers: [ 928306999 ], version: "4.2", designIdentifier: "205", shape: "ec30_2x2_lr" }, { id: "espressif-esp32s3devkitmv10", name: "ESP32-S3 DevKitM", company: "Espressif", description: "An ESP32-S3 dev-board with RGB LED and all pins", repo: "https://github.com/microsoft/devicescript-esp32", firmwareSource: "https://github.com/microsoft/devicescript-esp32/blob/main/boards/esp32s3/esp32s3_devkit_m.board.json", connector: "noConnector", link: "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/user-guide-devkitm-1.html", services: [ 413852154, 342028028, 341864092 ], productIdentifiers: [ 896848503 ], version: "1.0" }, { id: "microsoft-research-jmaccelerometer30v02", name: "JM Accelerometer", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 521405449 ], productIdentifiers: [ 872001670 ], version: "0.2", designIdentifier: "30", status: "deprecated" }, { id: "microsoft-research-jmaccelerometerv20", name: "JM Accelerometer v2.0", company: "Microsoft Research", description: "A 3-axis accelerometer. 16G range.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0i/profile/acc.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 521405449 ], productIdentifiers: [ 1020174761 ], status: "deprecated" }, { id: "microsoft-research-jmarcadebtnv20", name: "JM ArcadeBtn v2.0", company: "Microsoft Research", description: "Lets you connect a single arcade button with an LED.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0/profile/btnled.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 343122531 ], productIdentifiers: [ 886919574 ], status: "deprecated" }, { id: "microsoft-research-jmarcadecontrolsv20", name: "JM Arcade Controls v2.0", company: "Microsoft Research", description: "Lets you connect arcade buttons and joystick to a Jacdac network.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0/profile/arcade.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 501915758 ], productIdentifiers: [ 954450524 ], status: "deprecated" }, { id: "microsoft-research-jmbrainesp3248v02", name: "JM Brain ESP32", company: "Microsoft Research", connector: "edgeHighCurrentProvider", services: [ 342028028 ], productIdentifiers: [ 1067560617 ], transport: { type: "serial", vendorId: 12346 }, firmwares: [ { name: "IoT Uploader", url: "https://github.com/microsoft/pxt-jacdac/releases/latest/download/uploader-esp32s2.uf2", productIdentifier: 1067560617 } ], version: "0.2", designIdentifier: "48", status: "deprecated" }, { id: "microsoft-research-jmbuzzerv20", name: "JM Buzzer v2.0", company: "Microsoft Research", description: "A simple buzzer.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0/profile/snd.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 458731991 ], productIdentifiers: [ 854957595 ], status: "deprecated" }, { id: "microsoft-research-jmcrankbuttonv20", name: "JM Crank + Button", company: "Microsoft Research", description: "A rotary encoder with a push button.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0/profile/crank-btn.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 284830153, 343122531 ], productIdentifiers: [ 813927310 ], version: "2.0", status: "deprecated" }, { id: "microsoft-research-jmcrankv20", name: "JM Crank", company: "Microsoft Research", description: "A rotary encoder without a push button.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0/profile/crank.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 284830153 ], productIdentifiers: [ 866678795 ], version: "2.0", status: "deprecated" }, { id: "microsoft-research-jmkeyboardkey46v10", name: "JM Keyboard Key", company: "Microsoft Research", link: "https://github.com/microsoft/jacdac-padauk", services: [ 343122531 ], productIdentifiers: [ 1067229774 ], version: "1.0", designIdentifier: "46", status: "deprecated" }, { id: "microsoft-research-jmkeyboardkey46v11", name: "JM Keyboard Key", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 343122531 ], productIdentifiers: [ 911541523 ], version: "1.1", designIdentifier: "46", status: "deprecated" }, { id: "microsoft-research-jmmachinelearning", name: "JM Machine Learning", company: "Microsoft Research", description: "Lets you run machine learning models on data coming from Jacdac network.", repo: "https://github.com/microsoft/pxt-tensorflow", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 336566904, 496034245 ], status: "deprecated" }, { id: "microsoft-research-jmmotorv21", name: "JM Motor", company: "Microsoft Research", description: "Lets you control a single DC motor (up to 5V; yellow plastic ones work well).", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.1/profile/motor.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 385895640 ], productIdentifiers: [ 809626198 ], version: "2.1", status: "deprecated" }, { id: "microsoft-research-jmpinheader45v01", name: "JM Pin Header", company: "Microsoft Research", description: "A unregulated passive adapter from Jacdac to pin headers.", link: "https://github.com/microsoft/jacdac-msr-modules", productIdentifiers: [ 939230090 ], version: "0.1", designIdentifier: "45", status: "deprecated" }, { id: "microsoft-research-jmpower", name: "JM Power", company: "Microsoft Research", description: "Lets you supply power to Jacdac network from a MicroUSB connection (eg. a USB battery pack).", repo: "https://github.com/microsoft/jacdac-msr-modules", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 530893146 ], productIdentifiers: [ 815885628 ], status: "deprecated" }, { id: "microsoft-research-jmprotov20", name: "JM Proto", company: "Microsoft Research", description: "A prototype multi-function board.\n* ``0x3f9bc26a`` JM Touch-Proto v2.0", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0p/profile/proto.c", link: "https://github.com/microsoft/jacdac-msr-modules", productIdentifiers: [ 1052138004 ], version: "2.0", status: "deprecated" }, { id: "microsoft-research-jmpwmnpxv20", name: "JM PWM (npx)", company: "Microsoft Research", description: "A light-strip controller. Supports WS2812B, APA102, and SK9822.", repo: "https://github.com/microsoft/jacdac-msr-modules", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 309264608 ], productIdentifiers: [ 895762065 ], version: "2.0", status: "deprecated" }, { id: "microsoft-research-jmpwmnpxv21", name: "JM PWM (npx)", company: "Microsoft Research", description: "A light-strip controller with MicroUSB connector for power. Supports WS2812B, APA102, and SK9822.", repo: "https://github.com/microsoft/jacdac-msr-modules", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 309264608 ], productIdentifiers: [ 1013705700 ], version: "2.1", status: "deprecated" }, { id: "microsoft-research-jmpwmservov20", name: "JM PWM (Servo)", company: "Microsoft Research", description: "A controller for a 5V servo.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0/profile/servo.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 318542083 ], productIdentifiers: [ 816890446 ], version: "2.0", status: "deprecated" }, { id: "microsoft-research-jmpwmservov21", name: "JM PWM (Servo)", company: "Microsoft Research", description: "A controller for a 5V servo, with MicroUSB connector for power.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.1/profile/servo.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 318542083 ], productIdentifiers: [ 986140247 ], version: "2.1", status: "deprecated" }, { id: "microsoft-research-jmrgbledring37v20", name: "JM RGB LED Ring", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-rgb-led-ring-37-2.0/profile/npx.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 309264608 ], productIdentifiers: [ 892295887 ], version: "2.0", designIdentifier: "37", status: "deprecated" }, { id: "microsoft-research-jmrotarycontrolbuttonv10", name: "JM Rotary Control + Button", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-rotary-control-26-1.0/profile/rotary_ctrl.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 284830153, 343122531 ], productIdentifiers: [ 1060754715 ], version: "1.0", status: "deprecated" }, { id: "microsoft-research-jmslider49v10", name: "JM Slider", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-slider-49-1.0/profile/slider.c#", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 522667846 ], productIdentifiers: [ 981005156 ], version: "1.0", designIdentifier: "49", status: "deprecated" }, { id: "microsoft-research-jmsliderv20", name: "JM Slider", company: "Microsoft Research", description: "A linear potentiometer (slider).", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0/profile/slider.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 522667846 ], productIdentifiers: [ 1043615261 ], version: "2.0", status: "deprecated" }, { id: "microsoft-research-jmtemperaturehumidity18v10a", name: "JM Temperature + Humidity", company: "Microsoft Research", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-temp-humidity-18-1.0A/profile/env3.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 337754823, 382210232 ], productIdentifiers: [ 899442616 ], version: "1.0A", designIdentifier: "18", status: "deprecated" }, { id: "microsoft-research-jmtouchprotov20", name: "JM Touch-Proto", company: "Microsoft Research", description: "A multi-touch sensor based on proto board.", repo: "https://github.com/microsoft/jacdac-msr-modules", firmwareSource: "https://github.com/microsoft/jacdac-msr-modules/blob/main/targets/jm-v2.0p/profile/multitouch.c", link: "https://github.com/microsoft/jacdac-msr-modules", services: [ 416636459 ], productIdentifiers: [ 1067172458 ], version: "2.0", status: "deprecated" } ]; // src/jdom/catalog.ts function looksRandom(n) { const s = n.toString(16); const h = "0123456789abcdef"; for (let i = 0; i < h.length; ++i) { const hh = h[i]; if (s.indexOf(hh + hh + hh) >= 0) return false; } if (/f00d|dead|deaf|beef/.test(s)) return false; return true; } var DeviceCatalog = class extends JDEventSource { constructor(options) { super(); // eslint-disable-next-line @typescript-eslint/no-explicit-any this._specifications = devices_default; this.options = options || {}; } /** * Update specifications list and emit `change` event. * @param specifications */ update(specifications) { if (JSON.stringify(this._specifications) !== JSON.stringify(specifications)) { this._specifications = specifications.slice(0); this.emit(CHANGE); } } /** * Query device specifications * @param options * @returns */ specifications(options) { const { includeDeprecated, includeExperimental, transport } = options || {}; let r = this._specifications.slice(0); if (!includeDeprecated) r = r.filter((d) => d.status !== "deprecated"); if (!includeExperimental) r = r.filter((d) => d.status !== "experimental" && !!d.storeLink); if (transport) r = r.filter((d) => d.transport?.type === transport); return r; } /** * Query device specification from a product identifier * @param productIdentifier * @returns */ specificationFromProductIdentifier(productIdentifier) { if (isNaN(productIdentifier)) return void 0; const spec = this._specifications.find( (spec2) => spec2.productIdentifiers?.indexOf(productIdentifier) > -1 ); return spec; } specificationFromIdentifier(id) { if (id === void 0) return void 0; const spec = this._specifications.find((spec2) => spec2.id === id); return spec; } /** * Gets the list of devices that use this service class * @param serviceClass * @category Specification */ specificationsForService(serviceClass2, options) { if (isNaN(serviceClass2)) return void 0; return this.specifications(options).filter( (spec) => spec.services?.indexOf(serviceClass2) > -1 ); } /** * Gets the list of vendor ids for a given transport * @param type * @returns */ vendorIds(type) { const ids = this._specifications.filter((spec) => spec.transport?.type === type).map((spec) => spec.transport.vendorId); if (type === "serial") { const { serialVendorIds } = this.options; if (serialVendorIds) serialVendorIds.forEach((id) => ids.push(id)); } return unique(ids.filter((v) => !isNaN(v))); } /** * Checks if a vendor id match the transport * @param type * @param id * @returns */ matchVendorId(type, id) { if (isNaN(id)) return false; if (Flags.developerMode) return true; const ids = this.vendorIds(type); return ids.indexOf(id) > -1; } /** * Generates a unique firmware identifier * @returns */ uniqueFirmwareId(decimal) { const genFirmwareId = () => { const n = anyRandomUint32(1); if (n === void 0) return void 0; return n[0] & 268435455 | 805306368; }; let id = genFirmwareId(); while (id !== void 0 && (!looksRandom(id) || deviceCatalog.specificationFromProductIdentifier(id))) { id = genFirmwareId(); } return id !== void 0 && (decimal ? id.toString() : toFullHex([id])); } /** * Generate a unique service identifier * @returns */ uniqueServiceId() { const genServId = () => { const n = anyRandomUint32(1); if (n === void 0) return void 0; return n[0] & 268435455 | 268435456; }; let id = genServId(); while (id !== void 0 && (!looksRandom(id) || serviceSpecificationFromClassIdentifier(id))) { id = genServId(); } return id !== void 0 && toFullHex([id]); } /** * Generate a unique device identifier * @returns */ uniqueDeviceId() { const n = anyRandomUint32(2); return n !== void 0 && toFullHex([n[0], n[1]]); } }; function deviceCatalogImage(specification, size, docsRoot) { const sz = size || "full"; const root = docsRoot || DOCS_ROOT; return specification && `${root}images/devices/${identifierToUrlPath( specification.id )}.${sz}.jpg`; } function identifierToUrlPath(id) { if (!id) return id; const escape = (s) => s.replace(/[.:]/g, "").toLowerCase(); const parts = id.split(/-/g); if (parts.length === 1) return id.replace(/[.:]/g, "").toLowerCase(); return `${parts.slice(0, -1).map(escape).join("-")}/${escape( parts[parts.length - 1] )}`; } var deviceCatalog = new DeviceCatalog(); // src/jdom/bus.ts var BusInteractionMode = /* @__PURE__ */ ((BusInteractionMode2) => { BusInteractionMode2[BusInteractionMode2["Active"] = 0] = "Active"; BusInteractionMode2[BusInteractionMode2["Observer"] = 1] = "Observer"; BusInteractionMode2[BusInteractionMode2["Passive"] = 2] = "Passive"; return BusInteractionMode2; })(BusInteractionMode || {}); var JDBus = class extends JDNode { /** * Creates the bus with the given transport * @param sendPacket * @category Lifecycle */ constructor(transports, options) { super(); this._transports = []; this._bridges = []; this._devices = []; this._lastPingLoggerTime = 0; this._lastResetInTime = 0; this._restartCounter = 0; this._minLoggerPriority = 4 /* Silent */; this._gcDevicesFrozen = 0; this._serviceProviders = []; this._streaming = false; this._interactionMode = 0 /* Active */; this._autoConnect = false; this._resetIn = false; // self device is a client this._client = false; // self device is a dashboard this._dashboard = false; // acts as a proxy this._proxy = false; const { deviceId, scheduler, parentOrigin, client, disableRoleManager, dashboard, proxy, serviceProviderIdSalt, resetIn, serialVendorIds, services } = options || {}; this._roleManager = void 0; this.selfDeviceId = deviceId || randomDeviceId(); this.scheduler = scheduler || new WallClockScheduler(); this.parentOrigin = parentOrigin || "*"; this.serviceProviderIdSalt = serviceProviderIdSalt || ""; this._client = !!client; this._dashboard = !!dashboard; this._proxy = !!proxy; this._resetIn = !!resetIn; this.stats = new BusStatsMonitor(this); this.deviceCatalog = new DeviceCatalog({ serialVendorIds }); transports?.filter((tr) => !!tr).map((tr) => this.addTransport(tr)); this.on(SELF_ANNOUNCE, this.handleSelfAnnounce.bind(this)); this.on(DEVICE_ANNOUNCE, this.handleRealTimeClockSync.bind(this)); if (!disableRoleManager) this.on(DEVICE_CHANGE, this.handleRoleManager.bind(this)); if (services?.length) loadServiceSpecifications(services); this.start(); } configureBroadcastChannel() { if (typeof BroadcastChannel === "undefined" || typeof self === "undefined" || this._unsubscribeBroadcastChannel) return; const channel = new BroadcastChannel("jacdac"); const postConnectionState = () => { channel.postMessage({ id: this.selfDevice.shortId, event: "connectionstate", transports: this._transports.map((tr) => ({ type: tr.type, connectionState: tr.connectionState })) }); }; const unsubConnectionState = this.subscribe( CONNECTION_STATE, postConnectionState ); this._postBroadcastMessage = (event, msg) => { const bmsg = { id: this.selfDevice.shortId, event, ...msg || {} }; channel.postMessage(bmsg); }; const handleVisibilityChange = () => this._postBroadcastMessage("visibilitychange", { visibilityState: document.visibilityState }); const handleBroadcastMessage = async (msg) => { const { data } = msg; const { event } = data; switch (event) { case "visibilitychange": { const { visibilityState } = data; if (visibilityState === "visible") await this.disconnect(); else if (this.autoConnect) { await this.delay(2e3); await this.connect(true); } break; } case "disconnect": { await this.disconnect(); break; } case "connectionstate": { const { transports = [] } = data; transports.filter( (tr) => tr.connectionState === "connecting" /* Connecting */ ).forEach((ctr) => { this.transports.filter((tr) => tr.type === ctr.type).forEach((tr) => tr.disconnect()); }); } } }; channel.addEventListener("message", handleBroadcastMessage, false); document.addEventListener("visibilitychange", handleVisibilityChange); this._unsubscribeBroadcastChannel = () => { unsubConnectionState(); document.removeEventListener( "visibilitychange", handleVisibilityChange ); channel.removeEventListener("message", handleBroadcastMessage); channel.close(); }; handleVisibilityChange(); } /** * Broadcast a request to disconnect to all other javadac buses. */ broadcastDisconnectRequest() { this._postBroadcastMessage?.("disconnect", {}); } /** * Indicates that this bus acts as a client device * @category Lifecycle */ get client() { return this._client; } /** * Sets the client state * @category Lifecycle */ set client(value) { if (!!value !== this._client) { this._client = !!value; this.emit(CHANGE); } } /** * Indicates if the bus is a dashboard * @category Lifecycle */ get dashboard() { return this._dashboard; } /** * Gets the list of transports registers with the bus * @category Transports and Bridges */ get transports() { return this._transports.slice(0); } /** * Adds a transport to the bus. Returns unregistration handler * @category Transports and Bridges */ addTransport(transport) { if (!transport || this._transports.indexOf(transport) > -1) return; this._transports.push(transport); transport.setBus(this); const pre = () => this.preConnect(transport); this.on(CONNECTING, pre); const unmount = async () => { const i = this._transports.indexOf(transport); if (i > -1) { this.off(CONNECTING, pre); this._transports.splice(i, 1); this.emit(CONNECTION_STATE); await transport.disconnectBus(); } }; transport.on(DISPOSE, unmount); this.emit(CONNECTION_STATE); return unmount; } /** * Automatically try to connect to detected devices */ get autoConnect() { return this._autoConnect; } /** * Sets the auto-connected mode */ set autoConnect(value) { if (value !== this._autoConnect) { this._autoConnect = value; this.emit(CHANGE); if (this.disconnected && this._autoConnect && typeof document !== "undefined" && document.visibilityState === "visible") this.connect(true); } } /** * Gets the list of bridges registered with the bus * @category Transports and Bridges */ get bridges() { return this._bridges.slice(0); } /** * Add a bridge to the bus and returns a callback to remove it. * @param bridge * @returns callback to remove bridge * @category Transports and Bridges * @internal */ addBridge(bridge) { if (this._bridges.indexOf(bridge) < 0) { this._bridges.push(bridge); bridge.bus = this; this.emit(CHANGE); } return () => this.removeBridge(bridge); } removeBridge(bridge) { const i = this._bridges.indexOf(bridge); if (i > -1) { this._bridges.splice(i, 1); bridge.bus = void 0; this.emit(CHANGE); } } /** * Do not send any packet on the bus * @category Lifecycle */ get passive() { return this._interactionMode === 2 /* Passive */; } /** * Sets the passive state. A passive bus does not send any packets. * @category Lifecycle * @deprecated */ set passive(value) { this.interactionMode = value ? 2 /* Passive */ : 0 /* Active */; } /** * Determines how the bus interacts with services, including automatic polling of registers */ get interactionMode() { return this._interactionMode; } /** * Sets how the bus interacts with services, including automatic polling of registers */ set interactionMode(mode) { if (this._interactionMode !== mode) { this._interactionMode = mode; this.emit(CHANGE); } } /** * Indicates if reset in broadcast packets should be sent */ get resetIn() { return this._resetIn; } /** * Turns on or off reset in broadcast packets */ set resetIn(value) { this._resetIn = !!value; } preConnect(transport) { return Promise.all( this._transports.filter((t) => t !== transport).map((t) => t.disconnect()) ); } /** * Connects the bus going through the transports chronologically. Does nothing if already connected. * @param background connection was triggered automatically * @category Lifecycle */ async connect(background) { if (this.connected) return; for (const transport of this._transports) { await transport.connect(background); if (transport.connected) break; } this.emit(CHANGE); } /** * Disconnects the bus and any connected transport. * @category Lifecycle */ async disconnect() { for (const transport of this._transports) { await transport.disconnect(); } this.emit(CHANGE); } /** * Starts to process packets and updates the JDOM nodes * @category Lifecycle */ start() { this.configureBroadcastChannel(); if (!this._announceInterval) this._announceInterval = this.scheduler.setInterval(() => { if (!this.passive) this.emit(SELF_ANNOUNCE); }, 499); this.backgroundRefreshRegisters = true; if (!this._gcInterval) this._gcInterval = this.scheduler.setInterval( this.gcDevices.bind(this), JD_DEVICE_DISCONNECTED_DELAY ); } /** * Indicates if the bus is announcing and managing packets */ get running() { return !!this._announceInterval; } /** * Stops processing packets * @category Lifecycle */ async stop() { await this.disconnect(); if (this._unsubscribeBroadcastChannel) { this._postBroadcastMessage = void 0; this._unsubscribeBroadcastChannel(); this._unsubscribeBroadcastChannel = void 0; } if (this._announceInterval) { this.scheduler.clearInterval(this._announceInterval); this._announceInterval = void 0; } this.safeBoot = false; this.backgroundRefreshRegisters = false; if (this._gcInterval) { this.scheduler.clearInterval(this._gcInterval); this._gcInterval = void 0; } } /** * Stops the bus and all transport connections. * @category Lifecycle */ async dispose() { console.debug(`${this.id}: disposing.`); this.emit(DISPOSE); await this.stop(); this._transports.forEach((transport) => transport.dispose()); } /** * Indicates that the bus is sending commands keep devices in bootloader mode. * This property is signaled by CHANGE. * @category Lifecycle */ get safeBoot() { return !!this._safeBootInterval; } /** * Turn on or off the safe boot mode where the bus keeps devices in bootloader mode. * Triggers a CHANGE event. * @category Lifecycle */ set safeBoot(enabled) { if (enabled && !this._safeBootInterval) { this._safeBootInterval = this.scheduler.setInterval(() => { if (this._devices.some((d) => d.firmwareUpdater)) return; sendStayInBootloaderCommand(this); }, 50); this.emit(CHANGE); } else if (!enabled && this._safeBootInterval) { this.scheduler.clearInterval(this._safeBootInterval); this._safeBootInterval = void 0; this.emit(CHANGE); } } /** * Indicates if any of the transports is connected. * Some transports might be in the process of connecting or disconnecting. * @category Lifecycle */ get connected() { return this._transports.some((t) => t.connected); } /** * Indicates if any of the transports is disconnected. * Some transports might be in the process of connecting or disconnecting. * @category Lifecycle */ get disconnected() { return this._transports.every((t) => t.disconnected); } /** * Clears known devices and service providers (simulators). Optionally reset bus timestamp. * @param timestamp * @category Services */ clear(timestamp2 = 0) { if (this._serviceProviders?.length) { this._serviceProviders.forEach((host) => host.bus = void 0); this._serviceProviders = []; } this.clearDevices(); this.resetTime(timestamp2); this.emit(CHANGE); } /** * Clears current device list and let's them repopulate */ clearDevices() { const devs = this._devices; if (devs?.length) { this._devices = []; devs.forEach((dev) => { dev.disconnect(); this.emit(DEVICE_DISCONNECT, dev); this.emit(DEVICE_CHANGE, dev); }); } } /** * Gets a unique identifier for this node in the Jacdac DOM. * @category JDOM */ get id() { return this.nodeKind; } /** * Gets the bus name * @category JDOM */ get name() { return "bus"; } /** * Gets the bus name * @category JDOM */ get friendlyName() { return this.name; } /** * Gets the bus name * @category JDOM */ get qualifiedName() { return this.name; } /** * Returns the ``BUS_NODE_NAME`` * @category JDOM */ get nodeKind() { return BUS_NODE_NAME; } /** * Gets the default role manager service client, if any * @category Services */ get roleManager() { return this._roleManager; } /** * Specifies the current role manager */ setRoleManagerService(service) { if (this._roleManager?.service === service) return; if (this._roleManager) { this._roleManager.unmount(); this._roleManager = void 0; } if (service) { this._roleManager = new RoleManagerClient(service); this._roleManager.startRefreshRoles(); } this.emit(ROLE_MANAGER_CHANGE); this.emit(CHANGE); } /** * @internal */ toString() { return `bus: ${this._devices?.length || 0} devices, ${this._transports?.filter((tr) => tr.connected).map((tr) => tr.type).join(", ") || ""}`; } /** * Gets a detailled description of the devices and services connected to the bus * @returns * @internal */ describe(options) { return ` interaction_mode: ${this.interactionMode} auto_connect: ${this.autoConnect} reset_in: ${this.resetIn} last_reset_in_time: ${this.lastResetInTime} transport: ${this._transports.map((tr) => ` ${tr.type}: ${tr.connectionState}`).join("\n")} ${this._bridges?.length ? `bridges: ${this._bridges?.map( (tr) => ` ${tr.bridgeId}: recv ${tr.packetProcessed}, sent ${tr.packetSent}` ).join("\n")}` : ""}${this.devices(options).map( (dev) => `device: id: ${dev.shortId} (0x${dev.deviceId}) product: ${this.deviceCatalog.specificationFromProductIdentifier( dev.productIdentifier )?.name || "?"} (0x${dev.productIdentifier?.toString(16) || "?"}) firmware_version: ${dev.firmwareVersion || ""} uptime: ${dev.uptime || ""} stats: ${dev.stats.toString()} services: ${dev.services().slice(1).map( (srv) => [ ` ${srv.specification?.shortName || srv.name} (0x${srv.serviceClass.toString(16)})`, ...srv.registers().filter((reg) => !!reg.data).map( (reg) => ` ${reg.specification?.kind || "reg"} ${reg.name}${reg.needsRefresh ? "*" : ""}: ${reg.humanValue} (${toHex2(reg.data)}) ${reg.notImplemented ? "- not implemented" : ""} last data: ${reg.lastDataTimestamp | 0 || ""}, last get: ${reg.lastGetTimestamp | 0 || ""}, last set: ${reg.lastSetTimestamp | 0 || ""}, last gets attempts: ${reg.lastGetAttempts},` ), ...srv.events.filter((ev) => ev.count > 0).map((ev) => ` event ${ev.name}: ${ev.count}`) ].join("\n") ).join("\n")} ` ).join("\n")}`; } /** * Resolves a JDOM node from an identifier * @param id node identifier * @returns node if found, undefined otherwise * @category JDOM */ node(id) { const resolve = () => { const m = /^(?bus|device|service|register|event|field)(:(?\w+)(:(?\w+)(:(?\w+(:(?\w+))?))?)?)?$/.exec( id ); if (!m) return void 0; const type = m.groups["type"]; const dev = m.groups["dev"]; const srv = parseInt(m.groups["srv"], 16); const reg = parseInt(m.groups["reg"], 16); const idx = parseInt(m.groups["idx"], 16); switch (type) { case BUS_NODE_NAME: return this; case DEVICE_NODE_NAME: return this.device(dev, true); case SERVICE_NODE_NAME: return this.device(dev, true)?.service(srv); case REGISTER_NODE_NAME: return this.device(dev, true)?.service(srv)?.register(reg); case EVENT_NODE_NAME: return this.device(dev, true)?.service(srv)?.event(reg); case FIELD_NODE_NAME: return this.device(dev, true)?.service(srv)?.register(reg)?.fields[idx]; } console.info(`node ${id} not found`); return void 0; }; const node = resolve(); return node; } resetTime(delta = 0) { this.scheduler.resetTime(delta); this.emit(CHANGE); } /** * Gets the current bus-relavite time in milliseconds * @category Scheduling */ get timestamp() { return this.scheduler.timestamp; } /** * Indicates if the bus should force all sensors to stream * @category Lifecycle */ get streaming() { return this._streaming; } /** * Sets automatic streaming on and off * @category Lifecycle */ set streaming(value) { this._streaming = value; } /** * Creates a promise that awaits for the given duration using the bus scheduler * @category Scheduling */ delay(millis, value) { return new Promise( (resolve) => this.scheduler.setTimeout(() => resolve(value), millis) ); } /** * Gets the current desired minimum logger verbosity on the bus * @category Diagnostics */ get minLoggerPriority() { return this._minLoggerPriority; } /** * Sets the current desired minimum logger verbosity on the bus * @category Diagnostics */ set minLoggerPriority(priority) { if (priority !== this._minLoggerPriority) { this._lastPingLoggerTime = -Infinity; this._minLoggerPriority = priority; this.emit(CHANGE); } } /** * Returns undefined * @category JDOM */ get parent() { return void 0; } async handleRealTimeClockSync(device) { if (device.hasService(SRV_REAL_TIME_CLOCK)) await RealTimeClockServer.syncTime(this); } handleRoleManager(dev) { if (this._roleManager?.service?.disposed) { this.setRoleManagerService(void 0); } if (!dev.connected && this._roleManager?.service?.device === dev) { this.setRoleManagerService(void 0); } if (!this.roleManager) { const service = this.services({ serviceClass: SRV_ROLE_MANAGER })[0]; if (service) this.setRoleManagerService(service); } } async sendFrameAsync(frame) { if (frame._jacdac_timestamp === void 0) frame._jacdac_timestamp = this.timestamp; if (isLargeFrame(frame)) { this.processLargeFrame(frame); return; } this.emit(FRAME_SEND, frame); this.processFrame(frame, frame._jacdac_sender); await Promise.all( this._transports.map( (transport) => transport.sendPacketWhenConnectedAsync(frame) ) ); } /** * Sends a packet to the bus * @param packet packet to send * @internal */ async sendPacketAsync(packet) { packet.timestamp = this.timestamp; if (Flags.trace) packet.meta[META_TRACE] = stack(); if (this.passive) return; this.emit(PACKET_SEND, packet); await this.sendFrameAsync(packet.toBuffer()); } /** * Gets the list of known firmware blobs * @category Firmware */ get firmwareBlobs() { return this._firmwareBlobs; } /** * Sets the list of known firmware blobs * @category Firmware */ set firmwareBlobs(blobs) { this._firmwareBlobs = blobs; this.emit(FIRMWARE_BLOBS_CHANGE); this.emit(CHANGE); } /** * Gets the current list of known devices on the bus * @category Services */ devices(options) { if (options?.serviceName && options?.serviceClass > -1) throw Error("serviceClass and serviceName cannot be used together"); const sc = options?.serviceClass > -1 ? options?.serviceClass : serviceClass(options?.serviceName); let r = this._devices.slice(0); if (sc > -1) r = r.filter((s) => s.hasService(sc)); if (options?.ignoreInfrastructure) r = r.filter( (s) => s.deviceId !== this.selfDeviceId && s.serviceClasses.indexOf(SRV_INFRASTRUCTURE) < 0 ); if (options?.announced) r = r.filter((s) => s.announced); if (options?.ignoreSimulators) r = r.filter((r2) => !this.findServiceProvider(r2.deviceId)); if (options?.productIdentifier) r = r.filter((r2) => !!r2.productIdentifier); if (options?.physical) r = r.filter((r2) => !!r2.isPhysical); if (options?.lost !== void 0) r = r.filter((r2) => !!r2.lost === !!options?.lost); return r; } /** * Gets the current list of service providers on the bus * @category Services */ serviceProviders() { return this._serviceProviders.slice(0); } /** * Get a service providers for a given device * @param deviceId * @category Services */ findServiceProvider(deviceId) { return this._serviceProviders.find((d) => d.deviceId === deviceId); } /** * Adds the service provider to the bus and returns the associated devoce * @param provider instance to add * @category Services */ addServiceProvider(provider) { if (provider && this._serviceProviders.indexOf(provider) < 0) { this._serviceProviders.push(provider); provider.bus = this; this.emit(SERVICE_PROVIDER_ADDED, provider); this.emit(CHANGE); } return this.device(provider.deviceId); } /** * Removes the service provider from the bus * @param provider instance to remove * @category Services */ removeServiceProvider(provider) { if (!provider) return; const i = this._serviceProviders.indexOf(provider); if (i > -1) { this.removeDevice(provider.deviceId); this._serviceProviders.splice(i, 1); provider.bus = void 0; this.emit(SERVICE_PROVIDER_REMOVED, provider); this.emit(CHANGE); } } /** * Removes all service providers from the bus * @category Services */ clearServiceProviders() { this._serviceProviders.forEach((provider) => { this.removeDevice(provider.deviceId); provider.bus = void 0; this.emit(SERVICE_PROVIDER_REMOVED, provider); }); this._serviceProviders = []; this.emit(CHANGE); } /** * Remove a device client by identifier * @param deviceId * @category Devices */ removeDevice(deviceId) { const devi = this._devices.findIndex((d) => d.deviceId === deviceId); if (devi > -1) { const dev = this._devices[devi]; this._devices.splice(devi, 1); dev.disconnect(); this.emit(DEVICE_DISCONNECT, dev); this.emit(DEVICE_CHANGE, dev); } } /** * Gets the list of devices * @category JDOM */ get children() { return this.devices(); } /** * Gets the current list of services from all the known devices on the bus * @category Services */ services(options) { return arrayConcatMany( this.devices(options).map((d) => d.services(options)) ); } /** * Gets a device on the bus * @param id device identifier to query * @param skipCreate do not create new device if missing * @param pkt packet that generated this device query * @category Services */ device(id, skipCreate, pkt) { if (id === "0000000000000000" && !skipCreate) { console.warn("jacdac: trying to access device 0000000000000000"); return void 0; } let d = this._devices.find((d2) => d2.deviceId == id); if (!d && !skipCreate) { if (this.devicesFrozen) { console.debug(`info`, `devices frozen, dropping ${id}`); return void 0; } d = new JDDevice(this, id, pkt); this._devices.push(d); if (Flags.diagnostics) console.debug( `${id === this.selfDeviceId ? "self" : "new"} device ${d.shortId} (${id})` ); this._devices.sort((l, r) => strcmp(l.deviceId, r.deviceId)); this.emit(DEVICE_CONNECT, d); this.emit(DEVICE_CHANGE, d); this.emit(CHANGE); } return d; } /** * Push a context to disable cleaning device that haven't issued packets recently. * @category Lifecycle */ pushDeviceFrozen() { this._gcDevicesFrozen++; } /** * Pop a context to disable cleaning device that haven't issued packets recently. * @category Lifecycle */ popDeviceFrozen() { this._gcDevicesFrozen = Math.max(0, this._gcDevicesFrozen - 1); } /** * Indicates if the device list if currently frozen. * @category Lifecycle */ get devicesFrozen() { return this._gcDevicesFrozen > 0; } gcDevices() { this.emit(DEVICE_CLEAN); if (this.devicesFrozen) { console.debug("devices frozen"); return; } const LOST_DELAY = JD_DEVICE_LOST_DELAY; const DISCONNECTED_DELAY = JD_DEVICE_DISCONNECTED_DELAY; const now = this.timestamp; const lostCutoff = now - LOST_DELAY; const disconnectedCutoff = now - DISCONNECTED_DELAY; let changed = false; const devs = this._devices.slice(0); while (devs.length) { const dev = devs.pop(); if (dev === this.selfDevice) continue; if (dev.firmwareUpdater) continue; if (dev.lastSeen < disconnectedCutoff) { const i = this._devices.indexOf(dev); if (i > -1) this._devices.splice(i, 1); dev.disconnect(); this.emit(DEVICE_DISCONNECT, dev); this.emit(DEVICE_CHANGE, dev); changed = true; } else if (dev.lastSeen < lostCutoff) { dev.lost = true; changed = true; } } if (changed) { this.emit(CHANGE); } } /** * Ingests and process a packet received from the bus. * @param pkt a jacdac packet * @internal */ processPacket(pkt) { const frame = pkt.toBuffer(); if (!frame._jacdac_timestamp) frame._jacdac_timestamp = this.timestamp; this.emit(FRAME_PROCESS, pkt.toBuffer()); this.processPacketCore(pkt); } // this is called by sendFrameAsync() internally and by transports processFrame(frame, sender, skipCrc = false) { if (sender) frame._jacdac_sender = sender; if (frame._jacdac_timestamp === void 0) frame._jacdac_timestamp = this.timestamp; if (isLargeFrame(frame)) { this.processLargeFrame(frame); return; } this.emit(FRAME_PROCESS, frame); for (const pkt of Packet.fromFrame( frame, frame._jacdac_timestamp, skipCrc )) { if (frame._jacdac_replay) pkt.replay = true; this.processPacketCore(pkt); } } processLargeFrame(frame) { if (!this.passive) { const did = toHex2(frame.slice(8, 16)); const device = this.device(did, true); if (device) { const [topic, data] = jdunpack( frame.slice(16), "z b" ); const { si, command } = /^jd\/(?\d+)\/(?.+)$/.exec(topic)?.groups || {}; const serviceIndex = parseInt(si); if (serviceIndex > 0) { const service = device.service(serviceIndex); const twin = service?.twin; if (twin) twin.processLargeFrame(command, data); } } } this.emit(FRAME_PROCESS_LARGE, frame); } processPacketCore(pkt) { pkt.assignDevice(this); if (!pkt.isMultiCommand && !pkt.device) { if (Flags.diagnostics) console.debug(`unknown pkt device ${pkt.deviceIdentifier}`, { pkt }); this.emit(PACKET_RECEIVE_NO_DEVICE, pkt); return; } this.emit(PACKET_PRE_PROCESS, pkt); let isAnnounce = false; if (!pkt.device) { } else if (pkt.isCommand) { if (pkt.deviceIdentifier == this.selfDeviceId) { if (pkt.requiresAck) { const ack = Packet.onlyHeader(pkt.crc); ack.serviceIndex = JD_SERVICE_INDEX_CRC_ACK; ack.deviceIdentifier = this.selfDeviceId; ack.sendReportAsync(this.selfDevice); } } pkt.device.processPacket(pkt); } else { pkt.device.lastSeen = pkt.timestamp; if (pkt.serviceIndex == JD_SERVICE_INDEX_CTRL) { if (pkt.serviceCommand == CMD_ADVERTISEMENT_DATA) { isAnnounce = true; pkt.device.processAnnouncement(pkt); } else if (pkt.isMultiCommand && pkt.serviceCommand == (CMD_SET_REG | 128 /* ResetIn */)) { this._lastResetInTime = this.timestamp; } } pkt.device.processPacket(pkt); } this.emit(PACKET_PROCESS, pkt); if (isAnnounce) { this.emit(PACKET_RECEIVE_ANNOUNCE, pkt); } else { this.emit(PACKET_RECEIVE, pkt); if (pkt.isEvent && !pkt.isRepeatedEvent) this.emit(PACKET_EVENT, pkt); else if (pkt.isReport) this.emit(PACKET_REPORT, pkt); } } /** * Gets the current list of service specifications */ get serviceSpecifications() { return serviceSpecifications(); } /** * Updates the current list of device and clears the bus * @param services */ setCustomServiceSpecifications(services) { const res = loadServiceSpecifications(services); if (res.changed) { this.clearDevices(); this.emit(CHANGE); } return res; } /** * Gets the virtual device created by this bus to handle pipes. * @category Services */ get selfDevice() { return this.device(this.selfDeviceId); } handleSelfAnnounce() { return Promise.all([ this.sendAnnounce(), this.sendResetIn(), this.sendPingLoggers() ]).then(() => { }); } async sendAnnounce() { if (this._restartCounter < 15) this._restartCounter++; const pkt = Packet.jdpacked( CMD_ADVERTISEMENT_DATA, "u32 r: u32", [ this._restartCounter | (this._client ? 2048 /* IsClient */ : 0) | 512 /* SupportsBroadcast */ | 1024 /* SupportsFrames */ | 256 /* SupportsACK */, [ [SRV_INFRASTRUCTURE], this._dashboard ? [SRV_DASHBOARD] : void 0, this._proxy ? [SRV_PROXY] : void 0 ].filter((sc) => !!sc) ] ); pkt.serviceIndex = JD_SERVICE_INDEX_CTRL; pkt.deviceIdentifier = this.selfDeviceId; await pkt.sendReportAsync(this.selfDevice); } get lastResetInTime() { return this._lastResetInTime; } async sendResetIn() { if (!this.resetIn) return; if (!this.devices({ ignoreInfrastructure: true }).length) return; this._lastResetInTime = this.timestamp; const rst = Packet.jdpacked( CMD_SET_REG | 128 /* ResetIn */, "u32", [RESET_IN_TIME_US] ); await rst.sendAsMultiCommandAsync(this, SRV_CONTROL); } async sendStopStreaming() { const readingRegisters = this.services({ announced: true, ignoreInfrastructure: true }).map( (srv) => srv.readingRegister && srv.register(3 /* StreamingSamples */) ).filter((reg) => !!reg); await Promise.all( readingRegisters.map((reg) => reg.sendSetPackedAsync([0])) ); } async sendPingLoggers() { if (this._minLoggerPriority < 4 /* Silent */ && this.timestamp - this._lastPingLoggerTime > PING_LOGGERS_POLL && this.devices({ ignoreInfrastructure: true, serviceClass: SRV_LOGGER }).length > 0) { this._lastPingLoggerTime = this.timestamp; const pkt = Packet.jdpacked( CMD_SET_REG | 128 /* MinPriority */, "u8", [this._minLoggerPriority] ); await pkt.sendAsMultiCommandAsync(this, SRV_LOGGER); } } /** * Indicates if registers are automatically refreshed in the background. * @category Services */ get backgroundRefreshRegisters() { return !!this._refreshRegistersInterval; } /** * Enables or disables automatically refreshing registers in the background. * @param enabled true to automatically refresh registers * @category Services */ set backgroundRefreshRegisters(enabled) { if (!!enabled !== this.backgroundRefreshRegisters) { if (!enabled) { if (this._refreshRegistersInterval) this.scheduler.clearInterval(this._refreshRegistersInterval); this._refreshRegistersInterval = void 0; } else { this._refreshRegistersInterval = this.scheduler.setInterval( this.handleRefreshRegisters.bind(this), REFRESH_REGISTER_POLL ); } } } /** * Cycles through all known registers and refreshes the once that have REPORT_UPDATE registered */ handleRefreshRegisters() { if (this.interactionMode !== 0 /* Active */) return; const devices = this._devices.filter( (device) => device.announced && // needs services !device.lost && // ignore lost devices // !device.hasService(SRV_PROXY) && // proxies run servers! !device.firmwareUpdater ); if (!devices.length) return; const registers = arrayConcatMany( devices.map( (device) => arrayConcatMany( device.services({ specification: true }).map( (service) => service.registers().filter((reg) => !reg.notImplemented).filter( (reg) => ( // automatic streaming this._streaming && reg.code === 257 /* Reading */ || // someone asked for the value reg.needsRefresh || // listening for updates reg.listenerCount(REPORT_RECEIVE) > 0 || reg.listenerCount(REPORT_UPDATE) > 0 ) ).filter( (reg) => !reg.data || !isConstRegister(reg.specification) ).filter( (reg) => !reg.specification?.optional || reg.lastGetAttempts < REGISTER_OPTIONAL_POLL_COUNT ) ) ) ) ); for (const register of registers) { const { lastGetAttempts, service, specification } = register; const noDataYet = !register.data; const age = this.timestamp - register.lastGetTimestamp; const backoff = lastGetAttempts; if (register.needsRefresh && lastGetAttempts == 0) { register.sendGetAsync(); } else if (isReading(specification) && isSensor(service.specification)) { const intervalRegister = service.register( 4 /* StreamingInterval */ ); let interval = intervalRegister?.uintValue; if (interval === void 0) { const preferredIntervalRegister = service.register( 258 /* StreamingPreferredInterval */ ); const preferredInterval = preferredIntervalRegister?.uintValue; interval = preferredInterval; if (interval === void 0) { if (intervalRegister && !intervalRegister.data && this.timestamp - intervalRegister.lastGetTimestamp > REGISTER_POLL_STREAMING_INTERVAL) intervalRegister.sendGetAsync(); if (preferredIntervalRegister && !preferredIntervalRegister.data && this.timestamp - preferredIntervalRegister.lastGetTimestamp > REGISTER_POLL_STREAMING_INTERVAL) preferredIntervalRegister.sendGetAsync(); } } if (interval === void 0) interval = specification.preferredInterval || STREAMING_DEFAULT_INTERVAL; const streamingSamplesRegister = service.register( 3 /* StreamingSamples */ ); if (streamingSamplesRegister) { const streamingSamplesAge = this.timestamp - streamingSamplesRegister.lastSetTimestamp; const midSamplesAge = interval * 255 >> 1; if (streamingSamplesAge > midSamplesAge) { streamingSamplesRegister.sendSetPackedAsync([255]); } } else if (noDataYet && age > 500) { register.sendGetAsync(); } } else { const volatile = !!specification?.volatile; const expiration = volatile ? Math.min( REGISTER_POLL_REPORT_VOLATILE_MAX_INTERVAL, REGISTER_POLL_REPORT_VOLATILE_INTERVAL * (1 << backoff) ) : Math.min( REGISTER_POLL_REPORT_MAX_INTERVAL, (noDataYet ? REGISTER_POLL_FIRST_REPORT_INTERVAL : REGISTER_POLL_REPORT_INTERVAL) * (1 << backoff) ); if (age > expiration) { register.sendGetAsync(); } } } this.emit(REFRESH); this._serviceProviders.map((host) => host.emit(REFRESH)); } /** * Runs a promise with a timeout. Returns undefined if timeout happens before of disconnection. * @param timeout duration to wait before declaring timeout * @param promise promise to wrap * @category Lifecycle */ withTimeout(timeout, promise) { return new Promise((resolve, reject) => { let done = false; const tid = setTimeout(() => { if (!done) { done = true; if (!this._transports.some((tr) => tr.connected)) { this.emit(TIMEOUT_DISCONNECT); resolve(void 0); } else { this.emit(TIMEOUT); this.emit(ERROR, "Timeout (" + timeout + "ms)"); resolve(void 0); } } }, timeout); promise.then( (v) => { if (!done) { done = true; clearTimeout(tid); resolve(v); } else { this.emit(LATE); } }, (e) => { if (!done) { done = true; clearTimeout(tid); reject(e); } } ); }); } }; // src/jdom/field.ts var JDField = class extends JDNode { /** * @internal */ constructor(member, index, specification) { super(); this.member = member; this.index = index; this.specification = specification; } /** * Gets the JDOM node identifier * @category JDOM */ get id() { return `${this.nodeKind}:${this.member.service.device.deviceId}:${this.member.service.serviceIndex.toString( 16 )}:${this.member.code.toString(16)}:${this.index.toString(16)}`; } /** * Gets the JDOM name * @category JDOM */ get name() { return this.specification.name === "_" ? this.member.specification.name : this.specification.name; } /** * @internal */ get children() { return []; } /** * Gets the JDOM qualified name * @category JDOM */ get qualifiedName() { return `${this.member.qualifiedName}.${this.name}`; } /** * Gets the JDOM parent * @category JDOM */ get parent() { return this.member; } /** * Gets the JDOM friendly name * @category JDOM */ get friendlyName() { const parts = [this.member.friendlyName]; if (this.specification.name !== "_") parts.push(this.name); return parts.join("."); } /** * @internal */ get dataTypeName() { const parts = [this.member.service.specification.shortName, this.name]; return parts.join("."); } /** * Gets the unit of the data stored in the field * @category Data */ get unit() { return this.specification.unit; } /** * Gets ``FIELD_NODE_NAME`` * @category JDOM */ get nodeKind() { return FIELD_NODE_NAME; } /** * @internal */ get decoded() { const decoded = this.member.decoded; return decoded?.decoded[this.index]; } /** * Gets the decoded field value * @category Data */ // eslint-disable-next-line @typescript-eslint/no-explicit-any get value() { return this.decoded?.value; } }; // src/jdom/packobject.ts function unpackedToObject(data, fields, defaultName) { if (!data || !fields) return void 0; const r = {}; for (let i = 0; i < data.length; ++i) { const field = fields[i]; const value = data[i]; const { name, startRepeats } = field; const prettyName = name === "_" && defaultName ? defaultName : name; if (startRepeats) { const repeatFields = fields.slice(i); r["repeat"] = value.map((rdata) => { const r2 = {}; for (let i2 = 0; i2 < repeatFields.length; ++i2) { const field2 = fields[i2]; const value2 = rdata[i2]; const { name: name2 } = field2; const prettyName2 = name2 === "_" && defaultName ? defaultName : name2; r2[prettyName2] = value2; } return r2; }); break; } else r[prettyName] = value; } return r; } function objectToUnpacked(pkt, msg) { if (!msg) return []; if (typeof msg === "number" || typeof msg === "string") return [msg]; else if (typeof msg === "boolean") return [msg ? 1 : 0]; else if (Array.isArray(msg)) { return msg; } else { const { fields } = pkt; const r = []; for (let i = 0; i < fields.length; ++i) { const field = fields[i]; const name = field.name === "_" ? pkt.name : field.name; const value = msg[name]; if (field.startRepeats) { const repeatFields = fields.slice(i); r.push( // eslint-disable-next-line @typescript-eslint/no-explicit-any value.map((vrow) => { const arow = []; for (let j = 0; j < repeatFields.length; ++j) { const rfield = repeatFields[j]; const rname = rfield.name; arow.push(vrow[rname]); } return arow; }) ); break; } else { r.push(value); } } return r; } } // src/jdom/register.ts var JDRegister = class extends JDServiceMemberNode { /** * @internal */ constructor(service, code) { super(service, code, isRegister2); this._lastSetTimestamp = -Infinity; this._lastGetTimestamp = -Infinity; this._lastGetAttempts = 0; this._needsRefresh = false; } /** * Clears all cached data from the register */ clearData() { this._lastReportPkt = void 0; this._lastGetTimestamp = -Infinity; this._lastGetAttempts = 0; this.emit(REPORT_RECEIVE, this); this.emitPropagated(REPORT_UPDATE, this); this.emit(CHANGE); } /** * Returns ``REGISTER_NODE_NAME`` * @category JDOM */ get nodeKind() { return REGISTER_NODE_NAME; } /** * Gets the list of field, if the specification is known * @category JDOM */ get fields() { if (!this._fields) this._fields = this.specification?.fields.map( (field, index) => new JDField(this, index, field) ); return this._fields.slice(); } /** * Gets the list of fields * @category JDOM */ get children() { return this.fields; } /** * Timestamp of the last ``register set`` packet * @category Packets */ get lastSetTimestamp() { return this._lastSetTimestamp; } /** * Timestamp of the last ``register get`` packet * @category Packets */ get lastGetTimestamp() { return this._lastGetTimestamp; } /** * Clears the get timestamp * @internal * @category Packets */ clearGetTimestamp() { this._lastGetTimestamp = -Infinity; } /** * Number of attempts to send a ``get`` packet without response * @category Packets */ get lastGetAttempts() { return this._lastGetAttempts; } /** * Send a message to set the register value * @param data packed data * @param autoRefresh immediately send a ``get`` packet * @returns * @category Packets */ async sendSetAsync(data, autoRefresh) { if (this.notImplemented) return; const cmd = CMD_SET_REG | this.code; const pkt = Packet.from(cmd, data); this._lastSetTimestamp = this.service.device.bus.timestamp; await this.service.sendPacketAsync(pkt, this.service.registersUseAcks); if (autoRefresh) this.scheduleRefresh(); } /** * Requests the value of the register by sending a ``get`` packet * @returns * @category Packets */ sendGetAsync() { if (this.notImplemented) return Promise.resolve(); if (isConstRegister(this.specification) && !!this.data) return Promise.resolve(); this._lastGetTimestamp = this.service.device.bus.timestamp; this._lastGetAttempts++; const cmd = CMD_GET_REG | this.code; return this.service.sendCmdAsync(cmd, void 0, this.service.registersUseAcks).then(() => { this.emit(GET_ATTEMPT); }); } /** * Send a message to set the register value * @param values message to pack and send * @param autoRefresh immediately send a ``get`` packet * @category Packets */ sendSetPackedAsync(values2, autoRefresh) { const fmt = this.specification?.packFormat; if (!fmt) throw new Error("unknown register data format"); return this.sendSetAsync(jdpack(fmt, values2), autoRefresh); } /** * Sends a message to set the register value as a bpolean * @param value * @param autoRefresh * @param autoRefresh immediately send a ``get`` packet * @category Packets */ sendSetBoolAsync(value, autoRefresh) { return this.sendSetPackedAsync([value ? 1 : 0], autoRefresh); } /** * Sends a message to set the register value as a string * @param value * @param autoRefresh * @param autoRefresh immediately send a ``get`` packet * @category Packets */ sendSetStringAsync(value, autoRefresh) { return this.sendSetPackedAsync([value || ""], autoRefresh); } /** * Gets the raw data from the last report packet * @category Data */ get data() { return this._lastReportPkt?.data; } /** * Gets the timestamp when received the last report with data * @category Data */ get lastDataTimestamp() { return this._lastReportPkt?.timestamp; } /** * Get the data from the last report packet, unpacked according to the specification. * @category Data */ get unpackedValue() { const d = this.data; const fmt = this.specification?.packFormat; try { return d && fmt && jdunpack(this.data, fmt); } catch (e) { console.error(e); return void 0; } } /** * Gets the data from the last report packet, unpacked and hydrated into an object. * @category Data */ get objectValue() { const { specification } = this; return unpackedToObject( this.unpackedValue, specification?.fields, specification.name ); } /** * Gets the data as a signed integer * @category Data */ get intValue() { const d = this.data; return d && intOfBuffer(d); } /** * Gets the data as a unsigned integer * @category Data */ get uintValue() { const d = this.data; return d && uintOfBuffer(d); } /** * Gets the data as a boolean * @category Data */ get boolValue() { if (this.data === void 0) return void 0; return !!this.intValue; } /** * Gets the data as a string * @category Data */ get stringValue() { const buf = this.data; if (buf === void 0) return void 0; let value; try { value = fromUTF8(uint8ArrayToString(buf)); } catch { value = uint8ArrayToString(buf); } return value; } /** * Gets a pretty printed represention of the data * @category Data */ get humanValue() { return this.decoded?.decoded?.map((field) => field.humanValue).join(","); } /** * @internal */ toString() { const d = this.data; return `${this.id} ${d ? toHex2(d) : ""}`; } /** * @internal */ get decoded() { return this._lastReportPkt?.decoded; } /** * Schedules to query the value for this register */ scheduleRefresh() { if (this.notImplemented) return; this._needsRefresh = true; } get needsRefresh() { return this._needsRefresh; } /** * Refresh the value of the register within a timeout * @param skipIfValue don't refresh if any data if available * @returns true if refresh OK, false if timeout or other error * @category Data */ refresh(skipIfValue) { if (this.notImplemented) return Promise.resolve(false); if (!!this.data && (skipIfValue || isConstRegister(this.specification))) return Promise.resolve(true); const bus = this.service.device.bus; return bus.withTimeout( REGISTER_REFRESH_TIMEOUT, new Promise((resolve, reject) => { this.once(REPORT_RECEIVE, () => { const f = resolve; resolve = null; f(true); }); this.sendGetAsync().then(() => bus.delay(REGISTER_REFRESH_RETRY_0)).then(() => { if (resolve) return this.sendGetAsync().then( () => bus.delay(REGISTER_REFRESH_RETRY_1) ); }).then(() => { if (resolve) return this.sendGetAsync(); }).catch((e) => reject(e)); }) ); } /** * @internal */ processPacket(pkt) { if (this.notImplemented) return; if (pkt.isRegisterGet) this.processReport(pkt); else if (pkt.isRegisterSet) { this.clearGetTimestamp(); } } setNotImplemented() { console.assert( !this._lastReportPkt, `register reported changed not implemented`, { register: this } ); super.setNotImplemented(); } processReport(pkt) { const updated = !bufferEq(this.data, pkt.data) || this._needsRefresh; this._lastReportPkt = pkt; this._lastGetAttempts = 0; this._lastGetTimestamp = this.service.device.bus.timestamp; this._needsRefresh = false; this.emit(REPORT_RECEIVE, this); if (updated) { this.emitPropagated(REPORT_UPDATE, this); this.emit(CHANGE); } } /** * @internal */ compareTo(b) { return this.code - b.code || this.service.compareTo(b.service); } }; // src/jdom/event.ts var JDEvent = class extends JDServiceMemberNode { /** * @internal */ constructor(service, code) { super(service, code, isEvent); this._count = 0; } /** * Returns the ``EVENT_NODE_NAME`` identifier * @category JDOM */ get nodeKind() { return EVENT_NODE_NAME; } /** * Gets the field node * @category Service Clients */ get fields() { if (!this._fields) this._fields = this.specification?.fields.map( (field, index) => new JDField(this, index, field) ); return this._fields.slice(); } /** * Gets the list of fields * @category JDOM */ get children() { return this.fields; } /** * Gets the raw data attached to the last event packet * @category Data */ get data() { return this._lastReportPkt?.data; } /** * Gets the unpacked data attached to the last event packet, if the event specification is known. * @category Data */ get unpackedValue() { const { packFormat: packFormat2 } = this.specification || {}; return packFormat2 && this._lastReportPkt?.jdunpack(packFormat2); } /** * Gets a counter of occurences for this event. * @category Data */ get count() { return this._count; } /** * Gets the timestamp of the last packet with data received for this event. * @category Data */ get lastDataTimestamp() { return this._lastReportPkt?.timestamp; } /** * @internal */ get decoded() { return this._lastReportPkt?.decoded; } /** * @internal */ processEvent(pkt) { this._lastReportPkt = pkt; this._count++; this.emitPropagated(EVENT, this); this.emit(CHANGE); } }; // src/jdom/service.ts var JDService = class extends JDNode { /** * @internal */ constructor(device, serviceIndex) { super(); this._reports = []; this._specification = null; // packets received since last announce this.registersUseAcks = false; this._clients = []; this.device = device; this.serviceIndex = serviceIndex; this.serviceClass = this.device.serviceClassAt(this.serviceIndex); const statusCodeChanged = this.event(4 /* StatusCodeChanged */); statusCodeChanged.on(CHANGE, () => { const statusCode = this.register(259 /* StatusCode */); statusCode?.scheduleRefresh(); }); } get disposed() { return this.device.service(this.serviceIndex) !== this; } /** * Gets the node identifier * @category JDOM */ get id() { return `${this.nodeKind}:${this.device.deviceId}:${this.serviceIndex.toString(16)}`; } /** * Gets the ``SERVICE_NODE_NAME`` identifier * @category JDOM */ get nodeKind() { return SERVICE_NODE_NAME; } /** * Gets the service name * @category JDOM */ get name() { return serviceShortIdOrClass(this.serviceClass, "x").toLowerCase(); } /** * Gets the service name and parent names and service instance index * @category JDOM */ get friendlyName() { const { instanceName, serviceInstanceIndex } = this; let r = `${this.device.friendlyName}.${this.name}`; if (instanceName) r += `.${instanceName}`; else if (serviceInstanceIndex > 0) r += `[${serviceInstanceIndex}]`; return r; } /** * Gets the service qualified name and service instance index * @category JDOM */ get qualifiedName() { return `${this.device.qualifiedName}.${this.name}[${this.serviceInstanceIndex}]`; } /** * Gets the index of this service class instance. Provides a stable ordering into a list of services. */ get serviceInstanceIndex() { return this.device.services({ serviceClass: this.serviceClass }).indexOf(this); } /** * Gets the device holding the service * @category JDOM */ get parent() { return this.device; } get role() { return this._role; } set role(value) { if (value !== this._role) { this._role = value; this.emit(ROLE_CHANGE); this.emit(CHANGE); } } report(identifier) { return this._reports.find((r) => r.registerIdentifier === identifier); } get reports() { return this._reports.slice(0); } get mixins() { const r = []; const { serviceClasses, serviceLength } = this.device; for (let i = this.serviceIndex + 1; i < serviceLength && isMixinService(serviceClasses[i]); ++i) { r.push(this.device.service(i)); } return r; } get isMixin() { return isMixinService(this.serviceClass); } get twin() { return this._twin; } set twin(server) { if (this._twin === server) return; if (this._twin) this._twin.twin = void 0; this._twin = server; server.twin = this; this.emit(CHANGE); } /** * Gets the ``reading`` register associated to this service, if the specification supports it. * @category Registers */ get readingRegister() { if (!this._readingRegister) { const pkt = this.specification?.packets.find((pkt2) => isReading(pkt2)); this._readingRegister = pkt && this.register(pkt.identifier); } return this._readingRegister; } /** * Gets the ``value`` register associated to this service, if the specification supports it. * @category Registers */ get valueRegister() { if (!this._valueRegister) { const pkt = this.specification?.packets.find((pkt2) => isValue(pkt2)); this._valueRegister = pkt && this.register(pkt.identifier); } return this._valueRegister; } /** * Gets the ``intensity`` register associated to this service, if the specification supports it. * @category Registers */ get intensityRegister() { if (!this._intensityRegister) { const pkt = this.specification?.packets.find( (pkt2) => isIntensity(pkt2) ); this._intensityRegister = pkt && this.register(pkt.identifier); } return this._intensityRegister; } /** * Gets the service instance name, if resolved * @category Control */ get instanceName() { const r = this.register(265 /* InstanceName */); return r?.stringValue; } /** * Resolves the service instance name, if resolved * @category Control */ async resolveInstanceName() { const r = this.register(265 /* InstanceName */); await r?.refresh(); return r?.stringValue; } /** * Gets the specification of the service. Undefined if unknown * @category Services */ get specification() { if (this._specification === null) this._specification = serviceSpecificationFromClassIdentifier( this.serviceClass ); return this._specification; } get events() { return this.specification?.packets.filter(isEvent).map((info) => this.event(info.identifier)) || []; } /** * Gets the list of registers in the service * @param options * @returns */ registers(options) { if (!this._registers) { const spec = this.specification; this._registers = (spec?.packets || []).filter(isRegister2).map((pkt) => new JDRegister(this, pkt.identifier)); } let regs = this._registers.slice(0); if (options?.ignoreNacks) regs = regs.filter((r) => !r.notImplemented); return regs; } /** * Gets the registers and events * @category JDOM */ get children() { return [...this.registers(), ...this.events]; } /** * Gets a register for the given code * @param registerCode register identifier as found in the specification * @returns a register instance (if found in specifiaction) * @category Registers */ register(registerCode) { if (isNaN(registerCode)) return void 0; this.registers(); let register = this._registers.find((reg) => reg.code === registerCode); if (!register) { const spec = this.specification; if (spec && !spec.packets.some( (pkt) => isRegister2(pkt) && pkt.identifier === registerCode )) { if (Flags.diagnostics && !isOptionalReadingRegisterCode(registerCode)) console.debug( `attempting to access register ${this}.${SystemReg[registerCode] || `0x${registerCode.toString(16)}`}` ); return void 0; } this._registers.push( register = new JDRegister(this, registerCode) ); } return register; } /** * Gets an event for the given code * @param eventCode event identifier as found in the specification * @returns a event instance (if found in specifiaction) * @category Events */ event(eventCode) { if (isNaN(eventCode)) return void 0; if (!this._events) this._events = []; let event = this._events.find((ev) => ev.code === eventCode); if (!event) { const spec = this.specification; if (spec && !spec.packets.some( (pkt) => isEvent(pkt) && pkt.identifier === eventCode )) { if (Flags.diagnostics) console.debug( `attempting to access event ${SystemEvent[eventCode] || `0x${eventCode.toString(16)}`}` ); return void 0; } this._events.push(event = new JDEvent(this, eventCode)); } return event; } /** * Send packet to the service server * @param pkt packet to send * @param ack acknolegment required * @category Packets */ async sendPacketAsync(pkt, ack) { pkt.device = this.device; pkt.serviceIndex = this.serviceIndex; if (ack !== void 0) pkt.requiresAck = !!ack; if (pkt.requiresAck) await this.device.sendPktWithAck(pkt); else await pkt.sendCmdAsync(this.device); this.emit(PACKET_SEND, pkt); } /** * Send a command to the service server * @param pkt packet to send * @param ack acknolegment required * @category Packets */ sendCmdAsync(cmd, data, ack) { const pkt = data ? Packet.from(cmd, data) : Packet.onlyHeader(cmd); return this.sendPacketAsync(pkt, ack); } /** * Packs values and sends command to the service server * @param cmd packet to send * @param values unpacked values, layed as specified * @param ack acknolegment required * @category Packets */ sendCmdPackedAsync(cmd, values2, ack) { const spec = this.specification.packets.find( (pkt) => pkt.kind === "command" && pkt.identifier === cmd ); const packFormat2 = spec?.packFormat; if (!packFormat2) throw new Error("Unknown packing format"); const data = values2 ? jdpack(packFormat2, values2) : void 0; return this.sendCmdAsync(cmd, data, ack); } /** * Send a command and await response to the service server * @param pkt packet to send * @param ack acknolegment required * @category Packets */ sendCmdAwaitResponseAsync(pkt, timeout = 500) { const { bus } = this.device; return new Promise((resolve, reject) => { const handleRes = (resp) => { if (resp.serviceCommand == pkt.serviceCommand) { this.off(REPORT_RECEIVE, handleRes); if (resolve) resolve(resp); resolve = null; } }; bus.delay(timeout).then(() => { if (!resolve) return; resolve = null; this.off(REPORT_RECEIVE, handleRes); reject( new JDError( `timeout (${timeout}ms) waiting for response to ${pkt}`, { code: ERROR_TIMEOUT } ) ); }); this.on(REPORT_RECEIVE, handleRes); this.sendPacketAsync(pkt); }); } /** * @internal */ processPacket(pkt) { this.emit(PACKET_RECEIVE, pkt); if (pkt.isRepeatedEvent) return; if (pkt.isReport) { this.emit(REPORT_RECEIVE, pkt); if (pkt.isRegisterGet) { const id = pkt.registerIdentifier; const reg = this.register(id); if (reg) reg.processPacket(pkt); } else if (pkt.isEvent) { const ev = this.event(pkt.eventCode); if (ev) ev.processEvent(pkt); } else if (pkt.serviceCommand === 3 /* CommandNotImplemented */) { const [serviceCommand, packetCrc] = pkt.jdunpack("u16 u16"); if (serviceCommand >> 12 === CMD_GET_REG >> 12 || serviceCommand >> 12 === CMD_SET_REG >> 12) { const regCode = serviceCommand & CMD_REG_MASK; const reg = this.registers().find((r) => r.code === regCode); reg?.setNotImplemented(); } } else if (pkt.isCommand) { } } else if (pkt.isRegisterSet) { const id = pkt.registerIdentifier; const reg = this.register(id); if (reg) reg.processPacket(pkt); } else if (pkt.isCommand) { this.emit(COMMAND_RECEIVE, pkt); } } /** * @internal */ compareTo(b) { return this.serviceClass - b.serviceClass || strcmp(this.device.deviceId, b.device.deviceId) || this.serviceIndex - b.serviceIndex; } get clients() { return this._clients?.slice(0) || []; } addClient(client) { if (client && this._clients.indexOf(client) < 0) { this._clients.push(client); this.emit(SERVICE_CLIENT_ADDED, client); } } removeClient(client) { const i = this._clients.indexOf(client); if (i > -1) { this._clients.splice(i, 1); this.emit(SERVICE_CLIENT_REMOVED, client); } } async receiveWithInPipe(cmd, packFormat2, timeout) { const inp = new InPipeReader(this.device.bus); await this.sendPacketAsync(inp.openCommand(cmd), true); const recv = []; for (const buf of await inp.readData(timeout)) { const values2 = jdunpack(buf, packFormat2); recv.push(values2); } return recv; } }; // src/jdom/ledcontroller.ts function trgbToValues(trgb) { return [ trgb >> 16 & 255, trgb >> 8 & 255, trgb & 255, trgb >> 24 & 255 ]; } var LEDController = class extends JDEventSource { constructor(service, command) { super(); this.service = service; this.command = command; this._announces = 0; } get color() { return this._color; } async setColor(color) { if (color !== this._color) { this._color = color; this._announces = 0; if (this._color !== void 0) { const data = jdpack( ControlCmdPack.SetStatusLight, trgbToValues(color) ); await this.service.sendCmdAsync(this.command, data); } this.emit(CHANGE); } } async blink(from, to, interval, repeat) { const { bus } = this.service.device; for (let i = 0; i < repeat; ++i) { await this.setColor(from); await bus.delay(interval - 1); await this.setColor(to); await bus.delay(interval - 1); } } processAnnouncement() { if (this._color === void 0) return; this._announces++; if (this._announces > 2) { this._color = void 0; this._announces = 0; this.emit(CHANGE); } } processPacket(pkt) { const [toRed, toGreen, toBlue] = jdunpack(pkt.data, ControlCmdPack.SetStatusLight); this._color = toRed << 16 | toGreen << 8 | toBlue; this.emit(CHANGE); } }; // src/jdom/device.ts var DeviceStatsMonitor = class extends JDEventSource { /** * @internal */ constructor() { super(); // counter this._receivedPackets = 0; this._restarts = 0; this._announce = 0; // horizon this._data = Array(15 << 2).fill(0).map(() => ({ received: 0, restartCounter: 0, restarts: 0 })); this._dataIndex = 0; } /** * Number of announce packets received by the device **/ get announce() { return this._announce; } /** * Average packet dropped per announce period * @category Statistics */ get dropped() { const r = this._data.filter((e) => !!e.restartCounter).reduce( (s, e) => s + Math.max(e.restartCounter - e.received, 0), 0 ) / this._data.length || 0; return r; } /** * Number of restarts within the last 64 announce packets */ get restarts() { const r = this._data.reduce((s, e) => s + e.restarts, 0); return r; } /** * Gets the current stats */ get current() { const { dropped, restarts, announce } = this; return { dropped, restarts, announce }; } /** * @internal */ processAnnouncement(pkt) { this._announce++; const { current: oldCurrent } = this; const received = this._receivedPackets; const restartCounter = pkt.data[2]; const restarts = this._restarts; this._data[this._dataIndex] = { received, restartCounter, restarts }; this._dataIndex = (this._dataIndex + 1) % this._data.length; this._receivedPackets = 0; this._restarts = 0; const { current } = this; if (oldCurrent.dropped !== current.dropped || oldCurrent.restarts !== current.restarts) this.emit(CHANGE); } /** * @internal */ // eslint-disable-next-line @typescript-eslint/no-unused-vars processPacket(pkt) { this._receivedPackets++; } /** * @internal */ processRestart() { this._restarts++; this._announce = 0; } toString() { const { current } = this; const { announce, dropped, restarts } = current; return `announce ${announce}, drop ${Math.floor( dropped )}, restart ${restarts}`; } }; var JDDevice = class extends JDNode { /** * @internal */ constructor(bus, deviceId, pkt) { super(); /** * Quality of service statistics for this device * @category Diagnostics */ this.stats = new DeviceStatsMonitor(); this.bus = bus; this.deviceId = deviceId; this.connected = true; this._lost = false; this._identifying = false; this.created = this.lastSeen = bus.timestamp; this._source = pkt?.sender; this._replay = !!pkt?.replay; } /** * Gets a random device id for the lifetime of this object. */ get anonymizedDeviceId() { if (!this._anonymizedId) this._anonymizedId = randomDeviceId(); return this._anonymizedId; } /** * Gets a description of the device. * @returns a descriptive string for this device * @category Diagnostics */ describe() { const ignoredServices = [SRV_CONTROL, SRV_LOGGER]; return this.toString() + (this.isPhysical ? "" : " (sim)") + ": " + this.services().filter((srv) => ignoredServices.indexOf(srv.serviceClass) < 0).map( (s) => s.instanceName || s.specification?.camelName || s.serviceClass.toString(16) ).join(", "); } /** * Gets a unique identifier for this device in the bus * @category JDOM */ get id() { return `${this.nodeKind}:${this.deviceId}`; } /** * Gets the short id of the device * @category JDOM */ get name() { return this.shortId; } /** * Identifies node as a device * @category JDOM */ get nodeKind() { return DEVICE_NODE_NAME; } /** * Indicates if the devices is a physical device, not emulated. * @category Transport */ get isPhysical() { return this._source === USB_TRANSPORT || this._source === BLUETOOTH_TRANSPORT || this._source === SERIAL_TRANSPORT || this._source === PACKETIO_TRANSPORT || this._source === WEBSOCKET_TRANSPORT; } /** * Indicates the source of packets * @category Transport */ get source() { return this._source; } /** * Indicates if the device is part of a trace replay * @category Transport */ get replay() { return this._replay; } /** * Gets the device short name * @category JDOM */ get friendlyName() { return this.shortId; } /** * Gets the device short name * @category JDOM */ get qualifiedName() { return this.shortId; } /** * Indicates if service information is available. * This happens after a announce packet has been received. * @category Lifecycle */ get announced() { return !!this._servicesData?.length; } /** * Gets the control announce flag from the annouce packet. * @category Control */ get announceFlags() { return this._servicesData ? read16(this._servicesData, 0) : 0; } /** * Gets the restart counter from the announce packet. * @category Control */ get restartCounter() { return this.announceFlags & 15 /* RestartCounterSteady */; } /** * Gets the status light announce flags from the announce packet. * @category Control */ get statusLightFlags() { return this.announceFlags & 48 /* StatusLightRgbFade */; } /** * Indicates if the device is announced as a client * @category Control */ get isClient() { return !!(this.announceFlags & 2048 /* IsClient */); } /** * Gets the number of packets sent since the last announce packet, * as read from the announce packet. * @category Control */ get packetCount() { return this._servicesData?.[2] || 0; } /** * Gets the device short identifier * @category JDOM */ get shortId() { if (!this._shortId) this._shortId = shortDeviceId(this.deviceId); return this._shortId; } /** * Gets the bus instance hosting this device. * @category JDOM */ get parent() { return this.bus; } /** * Gets the firmware information if any. * @category Firmware */ get firmwareInfo() { const ctrl = this.service(JD_SERVICE_INDEX_CTRL); const productIdentifier = ctrl?.register(385 /* ProductIdentifier */)?.uintValue; const bootloaderProductIdentifier = ctrl?.register( 388 /* BootloaderProductIdentifier */ )?.uintValue; const ready = productIdentifier !== void 0 || bootloaderProductIdentifier !== void 0; if (!ready) return void 0; const deviceId = this.deviceId; const name = ctrl?.register(384 /* DeviceDescription */)?.stringValue; const version = this.firmwareVersion; return { deviceId, name, version, productIdentifier, bootloaderProductIdentifier }; } refreshFirmwareInfo() { if (this.bus.interactionMode !== 0 /* Active */) return; const ctrl = this.service(JD_SERVICE_INDEX_CTRL); if (!ctrl) return; this.emitPropagated(DEVICE_FIRMWARE_INFO); const firmwareRegs = [ 385 /* ProductIdentifier */, 389 /* FirmwareVersion */, 388 /* BootloaderProductIdentifier */, 384 /* DeviceDescription */ ]; firmwareRegs.map((code) => ctrl.register(code)).filter((reg) => !!reg).forEach( (reg) => reg.once(REPORT_UPDATE, () => { this.emitPropagated(DEVICE_FIRMWARE_INFO); this.emitPropagated(CHANGE); }) ); } /** * Indicates if no packet from this device has been observed in a while. * @category Lifecycle */ get lost() { return this._lost; } /** * Sets the lost status * @category Lifecycle * @internal */ set lost(v) { if (!!v === this._lost) return; this._lost = !!v; if (this.lost) { this.emit(LOST); this.bus.emit(DEVICE_LOST, this); } else { this.emit(FOUND); this.bus.emit(DEVICE_FOUND, this); } this.emit(CHANGE); this.bus.emit(DEVICE_CHANGE, this); this.bus.emit(CHANGE); } /** * Firmware updater, defined if a firmware update is in progress */ get firmwareUpdater() { return this._firmwareUpdater; } /** * Sets the flashing sequence state * @category Firmware */ set firmwareUpdater(value) { if (value !== this._firmwareUpdater) { this._firmwareUpdater = value; if (!this._firmwareUpdater) { const reg = this.service(0)?.register( 389 /* FirmwareVersion */ ); reg?.clearData(); } this.emit(CHANGE); this.bus.emit(DEVICE_CHANGE, this); this.bus.emit(CHANGE); if (this._firmwareUpdater) this.bus.sendStopStreaming(); } } /** * Gets the number of events received by the service clients in this device * @category Lifecycle */ get eventCounter() { return this._eventCounter; } /** * @internal */ set eventCounter(v) { this._eventCounter = v; } /** * Indicates if the device contains at least one service matching the service class * @param serviceClass service class to match * @returns true if at least one service present * @category Services */ hasService(serviceClass2) { if (!this.announced) return false; if (serviceClass2 === 0) return true; for (let i = 4; i < this._servicesData.length; i += 4) { const sc = getNumber(this._servicesData, 11 /* UInt32LE */, i); if (isInstanceOf(sc, serviceClass2)) return true; } return false; } /** * Gets or allocates a pipe port * @param id identifier of the port * @returns a pipe port * @category Services */ port(id) { if (!this._ports) this._ports = {}; const key = id + ""; const ex = this._ports[key]; if (!ex) return this._ports[key] = {}; return ex; } /** * Indicates if the service is in bootloader mode * @category Services */ get bootloader() { return this.hasService(SRV_BOOTLOADER); } /** * Gets the number of services hosted by the device * @category Services */ get serviceLength() { if (!this.announced) return 0; return this._servicesData.length >> 2; } /** * Gets the service class at a given index * @param index index of the service * @returns service class * @category Services */ serviceClassAt(index) { if (index == 0) return 0; index <<= 2; if (!this.announced || index + 4 > this._servicesData.length) return void 0; return read32(this._servicesData, index); } /** * Gets the list of service classes * @category Services */ get serviceClasses() { const r = []; const n = this.serviceLength; for (let i = 0; i < n; ++i) r.push(this.serviceClassAt(i)); return r; } initServices(force) { if (force) this._services = void 0; if (!this._services && this._servicesData) { this._statusLight = void 0; const n = this.serviceLength; const s = []; for (let i = 0; i < n; ++i) s.push(new JDService(this, i)); this._services = s; this.lastServiceUpdate = this.bus.timestamp; this.refreshFirmwareInfo(); this.emitPropagated(CHANGE); } } /** * Gets the service client at the given service index * @param serviceIndex index of the service client * @returns service client * @category Services */ service(serviceIndex) { if (!this.announced) return void 0; this.initServices(); serviceIndex = serviceIndex | 0; return this._services && this._services[serviceIndex]; } /** * Gets a filtered list of service clients. * @param options filters for services * @returns services matching the filter * @category Services */ services(options) { if (!this.announced) return []; if (options?.serviceIndex >= 0) return [this.service(options?.serviceIndex)]; if (options?.serviceName && options?.serviceClass > -1) throw Error("serviceClass and serviceName cannot be used together"); let sc = serviceClass(options?.serviceName); if (sc === void 0 || sc < 0) sc = options?.serviceClass; if (sc === void 0) sc = -1; this.initServices(); let r = this._services?.slice() || []; if (sc > -1) r = r.filter((s) => s.serviceClass == sc); if (options?.specification) r = r.filter((s) => !!s.specification); if (options?.sensor) r = r.filter((s) => isSensor(s.specification)); const mixins = options?.mixins; if (mixins !== void 0) r = r.filter((s) => s.isMixin === mixins); return r; } /** * Gets the list of child services. * @category JDOM */ get children() { return this.services(); } /** * @internal */ sendCtrlCommand(cmd, payload = null) { const pkt = !payload ? Packet.onlyHeader(cmd) : Packet.from(cmd, payload); pkt.serviceIndex = JD_SERVICE_INDEX_CTRL; return pkt.sendCmdAsync(this); } /** * @internal */ processAnnouncement(pkt) { this.stats.processAnnouncement(pkt); const w0 = this._servicesData ? getNumber(this._servicesData, 11 /* UInt32LE */, 0) : 0; const w1 = getNumber(pkt.data, 11 /* UInt32LE */, 0); const restarted = w1 && (w1 & JD_ADVERTISEMENT_0_COUNTER_MASK) < (w0 & JD_ADVERTISEMENT_0_COUNTER_MASK); let servicesChanged = !bufferEq(pkt.data, this._servicesData, 4); this._servicesData = pkt.data; if (restarted) { this.stats.processRestart(); this.bus.emit(DEVICE_RESTART, this); this.emit(RESTART); servicesChanged = true; this._eventCounter = void 0; } if (servicesChanged) { this.initServices(true); this.lastServiceUpdate = pkt.timestamp; this.bus.emit(DEVICE_ANNOUNCE, this); this.emit(ANNOUNCE); } this.bus.emit(DEVICE_PACKET_ANNOUNCE, this); this.emit(PACKET_ANNOUNCE); if (servicesChanged) { this.bus.emit(DEVICE_CHANGE, this); this.bus.emit(CHANGE); this.emit(CHANGE); } this.statusLight?.processAnnouncement(); } /** * Check if the device is still there * @returns true if the device is there, undefined on timeout */ ping() { return this.bus.withTimeout( 200, new Promise((resolve, reject) => { this.once(PACKET_ANNOUNCE, () => { const f = resolve; resolve = null; if (f) f(true); }); this.sendCtrlCommand(0 /* Services */).then( () => { }, (err) => { if (resolve) { resolve = null; reject(err); } } ); }) ); } markRepeatedEvent(pkt) { if (!pkt.isEvent || !pkt.isReport) return; if (this.eventCounter === void 0) { this.eventCounter = pkt.eventCounter; return; } const ec = (this.eventCounter || 0) + 1; const ahead = pkt.eventCounter - ec & CMD_EVENT_COUNTER_MASK; const behind = ec - pkt.eventCounter & CMD_EVENT_COUNTER_MASK; const old = behind < 60; const missed5 = ahead < 5; const isahead = ahead > 0; if (isahead && (old || missed5)) { pkt.isRepeatedEvent = true; } else { this.eventCounter = pkt.eventCounter; } } /** * @internal */ processPacket(pkt) { this.markRepeatedEvent(pkt); this.stats.processPacket(pkt); this.lost = false; this.emit(PACKET_RECEIVE, pkt); if (pkt.isReport) this.emit(PACKET_REPORT, pkt); else if (pkt.isEvent) this.emit(PACKET_EVENT, pkt); const service = this.service(pkt.serviceIndex); if (service) service.processPacket(pkt); if (pkt.serviceIndex == JD_SERVICE_INDEX_CTRL && pkt.isCommand && pkt.serviceCommand == 132 /* SetStatusLight */) pkt.device.statusLight?.processPacket(pkt); } /** * @internal */ disconnect() { this.connected = false; this.emit(DISCONNECT); this.emit(CHANGE); } /** * Gets a controller for the status light. Returns undefined if the device does not support a status light. * @category Control */ get statusLight() { if (!this._statusLight && this.statusLightFlags !== 0 /* StatusLightNone */) this._statusLight = new LEDController( this.service(0), 132 /* SetStatusLight */ ); return this._statusLight; } /** * Sends an ``identify`` command to the device * @category Lifecycle */ async identify() { if (this._identifying) return; try { this._identifying = true; this.emit(CHANGE); const statusLight = this.statusLight; if (statusLight) await statusLight.blink(255, 0, 262, 4); else { const ctrl = this.service(0); await ctrl.sendCmdAsync(129 /* Identify */, void 0, false); await this.bus.delay(IDENTIFY_DURATION); } } catch (e) { this.emit(ERROR, e); } finally { this._identifying = false; this.emit(CHANGE); } } /** * Indicates the device should be identifying. * @category Lifecycle */ get identifying() { return this._identifying; } /** * Sends a ``reset`` command to the device * @category Lifecycle */ reset() { return this.sendCtrlCommand(130 /* Reset */); } /** * Send command to enter proxy/dongle mode * @returns */ startProxy() { return this.sendCtrlCommand(133 /* Proxy */); } /** * Tries to retrive the product identifier from the device * @param retry number of attempts * @returns promise that returns product identifier if received * @category Control */ async resolveProductIdentifier(retry = 0) { const register = this.service(0)?.register(385 /* ProductIdentifier */); if (!register) return void 0; while (retry-- >= 0 && register.data === void 0) await register.refresh(true); return register.uintValue; } /** * Returns the product identifier synchronously. If needed, tries to refresh the value in the background. * @category Control */ get productIdentifier() { const reg = this.service(0)?.register(385 /* ProductIdentifier */); const v = reg?.uintValue; if (reg && v === void 0) reg.scheduleRefresh(); return v; } /** * Gets the elapsed time since boot in milli-seconds * @category Control */ get uptime() { const reg = this.service(0)?.register(390 /* Uptime */); const v = reg?.unpackedValue?.[0]; if (reg && v === void 0) reg.scheduleRefresh(); let uptime = void 0; if (v !== void 0) { uptime = v / 1e3 + this.bus.timestamp - reg.lastDataTimestamp; } return uptime; } /** * Tries to retrive the firmware version from the device * @param retry number of attempts * @returns promise that returns firmware if received * @category Control */ async resolveFirmwareVersion(retry = 0) { const register = this.service(0)?.register(389 /* FirmwareVersion */); if (!register) return void 0; while (retry-- >= 0 && register.data === void 0) await register.refresh(true); return register.stringValue; } /** * Returns the firmware version synchronously. If needed, tries to refresh the value in the background. * @category Control */ get firmwareVersion() { const reg = this.service(0)?.register(389 /* FirmwareVersion */); const v = reg?.stringValue; if (reg && v === void 0) reg.scheduleRefresh(); return v; } /** * Indicates if the device is in proxy mode */ get proxy() { return this.serviceClasses.indexOf(SRV_PROXY) > -1; } /** * Indicates if the device is a unique brain */ get brain() { return this.serviceClasses.indexOf(SRV_UNIQUE_BRAIN) > -1; } initAcks() { if (this._ackAwaiting) return; let drops = 0; let resends = 0; this._ackAwaiting = []; const cleanUp = this.subscribe(PACKET_REPORT, (rep) => { if (!rep.isCRCAck) return; let numdone = 0; for (const aa of this._ackAwaiting) { if (aa.pkt && aa.pkt.crc == rep.serviceCommand) { aa.pkt = null; numdone++; aa.okCb(); } } if (numdone) this._ackAwaiting = this._ackAwaiting.filter((aa) => !!aa.pkt); }); const resend = () => { let numdrop = 0; for (const aa of this._ackAwaiting) { if (aa.pkt) { if (--aa.retriesLeft < 0) { drops++; aa.pkt.meta[META_ACK_FAILED] = true; aa.pkt = null; aa.errCb(); numdrop++; if (Flags.diagnostics) console.debug( `ack: ${this.shortId} drop ${aa.pkt} (${drops} drops, ${resends} resends)` ); } else { resends++; aa.pkt.sendCmdAsync(this); if (Flags.diagnostics) console.debug( `ack: ${this.shortId} resend ${aa.pkt} (${drops} drops, ${resends} resends)` ); } } } if (numdrop) this._ackAwaiting = this._ackAwaiting.filter((aa) => !!aa.pkt); if (Flags.diagnostics) console.debug( `ack: ${this.shortId} awaits ${this._ackAwaiting.length}` ); if (this._ackAwaiting.length > 0) { this.bus.scheduler.setTimeout( resend, Math.random() * (ACK_MAX_DELAY - ACK_MIN_DELAY) + ACK_MIN_DELAY ); } else { this._ackAwaiting = void 0; cleanUp(); } }; this.bus.scheduler.setTimeout(resend, 40); } /** * @internal */ sendPktWithAck(pkt) { pkt.requiresAck = !this.bus.passive; this.initAcks(); return new Promise((resolve, reject) => { const ack = { pkt, retriesLeft: 4, okCb: resolve, errCb: () => { const e = new JDError("no ACK for " + pkt.toString(), { code: ERROR_NO_ACK }); reject(e); } }; this._ackAwaiting.push(ack); pkt.sendCmdAsync(this); }); } /** * @internal */ async floodPing(numPkts = 100, size = 32) { const pkt = Packet.jdpacked(131 /* FloodPing */, "u32 u32 u8", [ numPkts, 4096, size ]); pkt.serviceIndex = JD_SERVICE_INDEX_CTRL; await this.sendPktWithAck(pkt); } }; // src/jdom/trace/traceplayer.ts var TracePlayer = class extends JDClient { constructor(bus, speed = 1) { super(); this.bus = bus; this.speed = speed; this._busStartTimestamp = 0; this._index = 0; this._lastProgressEmit = 0; this.tick = this.tick.bind(this); this.mount(() => this.stop()); } get running() { return !!this._interval; } get trace() { return this._trace; } set trace(t) { if (t !== this._trace) { this.stop(); this._trace = t; this.emit(CHANGE); } } /** * Gets the adjusted timestamp */ get elapsed() { return (this.bus.timestamp - this._busStartTimestamp) * this.speed; } get progress() { if (!this.trace) return 0; return Math.max(0, Math.min(1, this.elapsed / this.trace.duration)); } get length() { return this.trace?.length || 0; } start() { if (this._interval || !this._trace) return; this._busStartTimestamp = this.bus.timestamp; this._index = 0; this._interval = this.bus.scheduler.setInterval(this.tick, 50); this.emit(CHANGE); this.emitProgress(true); } stop() { if (this._interval) { this.bus.scheduler.clearInterval(this._interval); this._interval = void 0; this.emitProgress(true); this.emit(CHANGE); } } tick() { if (!this._trace) return; const busElapsed = this.elapsed; const packets = this.trace.frames; const packetStart = packets[0]?._jacdac_timestamp || 0; while (this._index < packets.length) { const packet = packets[this._index]; const packetElapsed = packet._jacdac_timestamp - packetStart; if (packetElapsed > busElapsed) break; const pkt = packet.slice(); pkt._jacdac_timestamp = this._busStartTimestamp + packetElapsed; pkt._jacdac_replay = true; this.bus.processFrame(pkt, void 0); this._index++; } this.emitProgress(); if (this._index >= packets.length) this.stop(); } emitProgress(force) { if (force || this.bus.timestamp - this._lastProgressEmit > 250) { this.emit(PROGRESS, this.progress); this._lastProgressEmit = this.bus.timestamp; } } }; // src/jdom/logparser.ts function parseTrace(contents) { const description = []; const packets = []; contents?.split(/\r?\n/).forEach((ln) => { const m = /^(\d+.?\d*)\s+([a-f0-9]{12,})/i.exec(ln); if (!m) { if (/^\s+at\s/.test(ln)) { const lastPacket = packets[packets.length - 1]; if (lastPacket) { let trace = lastPacket._jacdac_meta[META_TRACE] || ""; trace += ln + "\n"; lastPacket._jacdac_meta[META_TRACE] = trace; } } else { if (packets.length == 0) description.push(ln); } return; } const timestamp2 = parseInt(m[1]); const data = fromHex(m[2]); data._jacdac_meta = { [META_TRACE_DESCRIPTION]: ln.substring(m[0].length).trim() }; data._jacdac_timestamp = timestamp2; packets.push(data); }); if (packets.length) return new Trace(packets, { description: description.join("\n").trim() }); else return void 0; } function parseLogicLog(logcontents) { if (!logcontents) return void 0; const res = []; let frameBytes = []; let lastTime = 0; for (const ln of logcontents.split(/\r?\n/)) { let m = /^JD (\d+) ([0-9a-f]+)/i.exec(ln); if (m) { const f = fromHex(m[2]); f._jacdac_timestamp = parseInt(m[1]); res.push(f); continue; } m = /^([\d\.]+)\s+([0-9a-f]{32,512})\s+/i.exec(ln); if (m) { const f = fromHex(m[2]); f._jacdac_timestamp = parseFloat(m[1]) | 0, res.push(f); continue; } m = /^([\d.]+),(?:Async Serial,)?.*(0x[A-F0-9][A-F0-9])/.exec(ln); if (!m) continue; const tm = parseFloat(m[1]); if (lastTime && tm - lastTime > 0.1) { const f = new Uint8Array(frameBytes); f._jacdac_timestamp = lastTime * 1e3; f._jacdac_meta = { info: "timeout" }; res.push(f); frameBytes = []; lastTime = 0; } lastTime = tm; if (/(framing error|Error)/.test(ln)) { if (frameBytes.length > 0) { const f = new Uint8Array(frameBytes); f._jacdac_timestamp = lastTime * 1e3; res.push(f); } frameBytes = []; lastTime = 0; } else { frameBytes.push(parseInt(m[2])); } } return res; } function replayLogicLog(bus, frames, speed) { const player = new TracePlayer(bus, speed); player.trace = new Trace(frames); bus.clear(frames[0]?._jacdac_timestamp); player.start(); } // src/jdom/speedtest.ts async function packetSpeedTest(dev) { const pingCmd = CMD_GET_REG | 385 /* ProductIdentifier */; dev.on(PACKET_REPORT, onPacket); const t0 = Date.now(); let lastSend = Date.now(); let numpkt = 0; let timeouts = 0; let numrecv = 0; let done = false; await ask(); while (numpkt < 100) { await delay(50); const now = Date.now(); if (now - t0 > 3e3) break; if (now - lastSend > 100) { timeouts++; await ask(); } } done = true; await delay(250); dev.off(PACKET_REPORT, onPacket); const ms = Date.now() - t0; const pktsPerSecond = numpkt / (ms / 1e3); const dropRate = 100 * (numpkt - numrecv) / numpkt; return { msg: `${pktsPerSecond.toFixed(1)} pkts/s; ${dropRate.toFixed( 2 )}% dropped`, pktsPerSecond, dropRate }; async function ask() { lastSend = Date.now(); numpkt++; await dev.sendCtrlCommand(pingCmd); } async function onPacket(p) { if (p.serviceIndex == JD_SERVICE_INDEX_CTRL && p.serviceCommand == pingCmd) { numrecv++; if (!done) await ask(); } } } // src/jdom/command.ts function packArguments(info, args) { let repeatIdx = -1; let numReps = 0; let argIdx = 0; let dst = 0; const buf = new Uint8Array(256); for (let i = 0; i < info.fields.length; ++argIdx, ++i) { if (argIdx >= args.length && numReps > 0) break; const arg0 = argIdx < args.length ? args[argIdx] : 0; const fld = info.fields[i]; if (repeatIdx == -1 && fld.startRepeats) repeatIdx = i; const arg1 = fld.type == "string0" && typeof arg0 == "string" ? arg0 + "\0" : arg0; const arg = typeof arg1 == "boolean" ? arg1 ? 1 : 0 : typeof arg1 == "string" ? stringToUint8Array(toUTF8(arg1)) : arg1; if (typeof arg == "number") { const intVal = scaleFloatToInt(arg, fld); if (fld.storage == 0) throw new Error(`expecting ${fld.type} got number`); const fmt = numberFormatFromStorageType(fld.storage); setNumber(buf, fmt, dst, clampToStorage(intVal, fld.storage)); dst += sizeOfNumberFormat(fmt); } else { let size = Math.abs(fld.storage); if (typeof arg1 == "string") { if (size == 0) size = arg.length; const argCut = arg.slice(0, size); buf.set(argCut, dst); dst += size; } else if (size == 0 || size == arg.length) { buf.set(arg, dst); dst += arg.length; } else { throw new Error( `expecting ${Math.abs(fld.storage)} bytes; got ${arg.length}` ); } } if (dst >= JD_SERIAL_MAX_PAYLOAD_SIZE) throw new Error( `jacdac packet length too large, ${dst} > ${JD_SERIAL_MAX_PAYLOAD_SIZE} bytes` ); if (repeatIdx != -1 && i + 1 >= info.fields.length) { i = repeatIdx - 1; numReps++; } } const cmd = isRegister2(info) ? info.identifier | CMD_SET_REG : info.identifier; const pkt = Packet.from(cmd, buf.slice(0, dst)); if (info.kind != "report") pkt.isCommand = true; return pkt; } // src/jdom/packetfilter.ts function parsePacketFilter(bus, text) { if (!text) { return { source: text, props: { grouping: true }, filter: () => true }; } const flags = /* @__PURE__ */ new Set(); const serviceClasses = /* @__PURE__ */ new Set(); const pkts = /* @__PURE__ */ new Set(); const productIdentifiers = /* @__PURE__ */ new Set(); let repeatedAnnounce = void 0; let announce = void 0; let resetIn = void 0; let minPriority = void 0; let regGet = void 0; let regSet = void 0; let requiresAck = void 0; let ack = void 0; let log2 = void 0; let before = void 0; let after = void 0; const devices = {}; let grouping = true; let pipes = void 0; let port = void 0; let collapseAck = true; let collapsePipes = true; let collapseGets = true; let collapseNotImplemented = true; let errors = void 0; let selfDevice = void 0; text.split(/\s+/g).forEach((part) => { const [, prefix, , value] = /([a-z\-_]+)([:=]([^\s]+))?/.exec(part) || []; switch (prefix || "") { case "kind": case "k": if (!value) break; flags.add(value.toLowerCase()); break; case "service": case "srv": { if (!value) break; const service = serviceSpecificationFromName(value); const serviceClass2 = service?.classIdentifier || parseInt(value, 16); if (serviceClass2 !== void 0 && !isNaN(serviceClass2)) serviceClasses.add(serviceClass2); break; } case "announce": case "a": announce = parseBoolean(value); break; case "repeated-announce": case "ra": repeatedAnnounce = parseBoolean(value); break; case "self": selfDevice = parseBoolean(value); break; case "reset-in": case "ri": case "resetin": resetIn = parseBoolean(value); break; case "errors": errors = parseBoolean(value); break; case "min-priority": case "minpri": case "minpriority": case "mi": minPriority = parseBoolean(value); break; case "requires-ack": requiresAck = parseBoolean(value); break; case "ack": ack = parseBoolean(value); break; case "collapse-ack": collapseAck = parseBoolean(value); break; case "collapse-notimpl": case "collapse-not-implemeneted": collapseNotImplemented = parseBoolean(value); break; case "device": case "dev": case "to": case "from": { if (!value) break; const deviceId = bus.devices().find((d) => d.shortId === value || d.name === value)?.deviceId; if (deviceId) { const data = devices[deviceId] || (devices[deviceId] = { from: false, to: false }); if (prefix === "from") data.from = true; else if (prefix === "to") data.to = true; } break; } case "pid": case "product-identifier": { if (!value) return; const pid = parseInt(value.replace(/^0?x/, ""), 16); if (!isNaN(pid)) productIdentifiers.add(pid); break; } case "pkt": case "reg": case "register": case "cmd": case "command": case "ev": case "event": { if (!value) return; const id = parseInt(value.replace(/^0?x/, ""), 16); if (!isNaN(id)) pkts.add(id.toString(16)); pkts.add(value); break; } case "reg-get": case "get": regGet = parseBoolean(value); break; case "reg-set": case "set": regSet = parseBoolean(value); break; case "log": log2 = parseBoolean(value); break; case "before": before = parseTimestamp(value); break; case "after": after = parseTimestamp(value); break; case "grouping": grouping = parseBoolean(value); break; case "pipes": pipes = parseBoolean(value); break; case "collapse-pipe": case "collapse-pipes": collapsePipes = parseBoolean(value); break; case "collapse-get": case "collapse-gets": collapseGets = parseBoolean(value); break; case "port": port = parseInt(value); break; } }); const props = { announce, repeatedAnnounce, resetIn, minPriority, requiresAck, ack, collapseAck, collapseNotImplemented, log: log2, productIdentifiers: !!productIdentifiers.size && Array.from(productIdentifiers.keys()), flags: !!flags.size && Array.from(flags.keys()), regGet, regSet, devices, selfDevice, serviceClasses: !!serviceClasses.size && Array.from(serviceClasses.keys()), pkts: !!pkts.size && Array.from(pkts.keys()), before, after, grouping, pipes, collapsePipes, collapseGets, port, errors }; const filter = compileFilter(props); return { source: text, props, filter }; function parseBoolean(value) { if (value === "false" || value === "no") return false; else if (value === "true" || value === "yes" || !value) return true; else return void 0; } function parseTimestamp(value) { const t = parseInt(value); return isNaN(t) ? void 0 : t; } } function compileFilter(props) { const { announce, repeatedAnnounce, resetIn, minPriority, requiresAck, ack, log: log2, productIdentifiers, flags, regGet, regSet, devices, selfDevice, serviceClasses, pkts, before, after, pipes, port, errors } = props; const filters = []; if (before !== void 0) filters.push((pkt) => pkt.timestamp <= before); if (after !== void 0) filters.push((pkt) => pkt.timestamp >= after); if (announce !== void 0) filters.push((pkt) => pkt.isAnnounce === announce); if (repeatedAnnounce !== void 0) filters.push( (pkt) => (!pkt.isAnnounce || pkt.isRepeatedAnnounce) === repeatedAnnounce ); if (resetIn !== void 0) filters.push( (pkt) => !!(pkt.isRegisterSet && pkt.serviceClass === SRV_CONTROL && pkt.registerIdentifier === 128 /* ResetIn */) === resetIn ); if (minPriority !== void 0) filters.push( (pkt) => (pkt.isRegisterSet && pkt.serviceClass == SRV_LOGGER && pkt.registerIdentifier === 128 /* MinPriority */) === minPriority ); if (requiresAck !== void 0) filters.push((pkt) => pkt.requiresAck === requiresAck); if (ack !== void 0) filters.push((pkt) => pkt.isCRCAck === ack); if (flags) filters.push((pkt) => hasAnyFlag(pkt)); if (pipes !== void 0) filters.push((pkt) => pkt.isPipe); if (port !== void 0) filters.push((pkt) => pkt.pipePort === port); if (regGet !== void 0 && regSet !== void 0) filters.push( (pkt) => pkt.isRegisterGet === regGet && pkt.isRegisterSet === regSet ); else if (regGet !== void 0) filters.push((pkt) => pkt.isRegisterGet === regGet); else if (regSet !== void 0) filters.push((pkt) => pkt.isRegisterSet === regSet); if (log2 !== void 0) filters.push( (pkt) => (pkt.serviceClass === SRV_LOGGER && pkt.isReport) === log2 ); if (selfDevice !== void 0) { filters.push((pkt) => { const { device } = pkt; if (!device) return true; return device === device.bus.selfDevice === selfDevice; }); } if (Object.keys(devices).length) filters.push((pkt) => { if (!pkt.device) return false; const f = devices[pkt.device.deviceId]; return !!f && (!f.from || !pkt.isCommand) && (!f.to || pkt.isCommand); }); if (serviceClasses) { filters.push( (pkt) => serviceClasses.some( (serviceClass2) => isInstanceOf(pkt.serviceClass, serviceClass2) ) ); } if (pkts) { filters.push( (pkt) => pkts.indexOf(pkt.decoded?.info.identifier.toString(16)) > -1 || pkts.indexOf(pkt.decoded?.info.name) > -1 ); } if (productIdentifiers) filters.push((pkt) => { const fwid = pkt.device?.productIdentifier; return fwid === void 0 || productIdentifiers.indexOf(fwid) > -1; }); if (errors !== void 0) filters.push((pkt) => !!pkt.decoded?.error === errors); const filter = (pkt) => filters.every((filter2) => filter2(pkt)); return filter; function hasAnyFlag(pkt) { const k = pkt.decoded?.info.kind; return !!k && flags.indexOf(k) > -1; } } // src/jdom/trace/tracerecorder.ts var RECORDING_TRACE_MAX_ITEMS = 1e5; var TraceRecorder = class extends JDClient { constructor(bus) { super(); this.bus = bus; this.maxRecordingLength = RECORDING_TRACE_MAX_ITEMS; this.handleFrame = this.handleFrame.bind(this); this.mount(() => this._subscription?.()); } start() { if (this.recording) return; this._subscription = this.bus.subscribe(FRAME_PROCESS, this.handleFrame); this._trace = new Trace([], { maxLength: this.maxRecordingLength }); this.emit(START); this.emit(CHANGE); } stop() { if (!this.recording) return; this._subscription?.(); this._subscription = void 0; const t = this._trace; this._trace = void 0; this.emit(STOP); this.emit(CHANGE); return t; } get recording() { return !!this._trace; } get trace() { return this._trace; } handleFrame(pkt) { this._trace.addFrame(pkt); this.emit(FRAME_PROCESS, pkt); } }; // src/jdom/trace/traceview.ts var TRACE_MAX_ITEMS = 1e3; var FILTERED_TRACE_MAX_ITEMS = 100; var DUPLICATE_PACKET_MERGE_HORIZON_MAX_DISTANCE = 10; var DUPLICATE_PACKET_MERGE_HORIZON_MAX_TIME = 5e3; var TraceView = class extends JDClient { constructor(bus, filter, throttleDelay = 200) { super(); this.bus = bus; this.id = "v" + Math.random(); this._maxFilteredLength = FILTERED_TRACE_MAX_ITEMS; this._paused = false; this._packetFilter = void 0; this._filteredPackets = []; this._allPackets = []; this.silent = false; this._trace = new Trace([], { maxLength: TRACE_MAX_ITEMS }); this.handleFrame = this.handleFrame.bind(this); this.handlePacket = this.handlePacket.bind(this); this.handleFilterUpdate = this.handleFilterUpdate.bind(this); this.notifyPacketsChanged = throttle(() => { if (!this.silent) this.setFilteredPackets(); }, throttleDelay); this.mount(this.bus.subscribe(FRAME_PROCESS, this.handleFrame)); this.mount(this.bus.subscribe(PACKET_PROCESS, this.handlePacket)); this.mount(this.bus.subscribe(DEVICE_ANNOUNCE, this.handleFilterUpdate)); this.filter = filter; } /** * No new packet is added to the filtered view */ get paused() { return this._paused; } set paused(v) { if (v !== this._paused) { this._paused = v; if (!this._paused) { this.refreshFilter(); this.emit(CHANGE); } } } get trace() { return this._trace; } set trace(t) { if (t !== this._trace) { this._trace = t; this._allPackets = t.toPackets(this.bus); this.refreshFilter(); this.emit(CHANGE); } } get filteredPackets() { return this._filteredPackets; } get filter() { return this._filter; } set filter(f) { if (f !== this._filter) { this._filter = f; this.refreshFilter(); this.emit(CHANGE); } } get maxFilteredLength() { return this._maxFilteredLength; } set maxFilteredLength(v) { if (this._maxFilteredLength !== v) { this._maxFilteredLength = v; this.refreshFilter(); this.emit(CHANGE); } } setFilteredPackets() { this._filteredPackets = this._filteredPackets.slice( 0, this._filteredPackets.length > this.maxFilteredLength * 1.1 ? this.maxFilteredLength : this._filteredPackets.length ); this.emit(CHANGE); } clear() { this.trace = new Trace([], { maxLength: TRACE_MAX_ITEMS }); this._filteredPackets = []; this.setFilteredPackets(); this.emit(CHANGE); } handleFilterUpdate() { this.refreshFilter(); } refreshFilter() { this.id = "view" + Math.random(); this._packetFilter = parsePacketFilter(this.bus, this._filter); this._filteredPackets = []; const packets = this._allPackets; for (let i = packets.length - 1; i >= 0 && this._filteredPackets.length < this.maxFilteredLength; --i) { const pkt = packets[i]; if (this._packetFilter?.filter(pkt)) { this.addFilteredPacket(pkt); } } this._filteredPackets = this._filteredPackets.reverse(); this.notifyPacketsChanged?.(); } handlePacket(pkt) { if (this._paused) return; this._allPackets.push(pkt); if (this._allPackets.length > TRACE_MAX_ITEMS * 1.5) this._allPackets = this._allPackets.slice(-TRACE_MAX_ITEMS); if (this._packetFilter?.filter(pkt)) { this.addFilteredPacket(pkt); this.notifyPacketsChanged?.(); } } handleFrame(frame) { if (this._paused) return; this._trace.addFrame(frame); } addFilteredPacket(packet) { if (packet.meta[this.id]) return; packet.meta[this.id] = true; let filtered = true; const hash2 = toHex2(packet.toBuffer()); if (this._packetFilter?.props.grouping) { const old = this._filteredPackets.slice(0, DUPLICATE_PACKET_MERGE_HORIZON_MAX_DISTANCE).find( (p) => packet.timestamp - p.packet.timestamp < DUPLICATE_PACKET_MERGE_HORIZON_MAX_TIME && p.hash === hash2 ); if (old) { old.count++; filtered = false; } } if (packet.isCRCAck) { const pkts = this._allPackets; const crc2 = packet.serviceCommand; const did = packet.deviceIdentifier; const m = Math.max(0, pkts.length - TRACE_FILTER_HORIZON); for (let i = pkts.length - 1; i >= m; i--) { const old = pkts[i]; if (old.requiresAck && old.deviceIdentifier === did && old.crc === crc2) { old.meta[META_ACK] = packet; if (this._packetFilter?.props.collapseAck) filtered = false; break; } } } if (packet.serviceCommand === 3 /* CommandNotImplemented */) { const pkts = this._allPackets; const [sc, crc2] = packet.jdunpack("u16 u16"); const m = Math.max(0, pkts.length - TRACE_FILTER_HORIZON); for (let i = pkts.length - 1; i >= m; i--) { const old = pkts[i]; if (old.crc === crc2 && old.serviceCommand === sc) { old.meta[META_NOT_IMPLEMENTED] = packet; if (this._packetFilter?.props.collapseNotImplemented) filtered = false; break; } } } if (packet.isRegisterGet && packet.isReport && !packet.meta[META_GET]) { const pkts = this._allPackets; const did = packet.deviceIdentifier; const si = packet.serviceIndex; const rid = packet.registerIdentifier; const m = Math.max(0, pkts.length - TRACE_FILTER_HORIZON); for (let i = pkts.length - 1; i >= m; i--) { const old = pkts[i]; if (old.isRegisterGet && old.isCommand && old.deviceIdentifier === did && old.serviceIndex === si && old.registerIdentifier === rid) { packet.meta[META_GET] = old; if (this._packetFilter?.props.collapseGets) { this._filteredPackets.splice(i, 1); } break; } } } if (this._packetFilter?.props.collapsePipes && packet.isPipe && packet.isCommand) { const pkts = this._filteredPackets; const m = Math.min(pkts.length, TRACE_FILTER_HORIZON); const port = packet.pipePort; const did = packet.deviceIdentifier; for (let i = 0; i < m; ++i) { const old = pkts[i].packet; if (old.deviceIdentifier === did && old.pipePort === port) { let pipePackets = old.meta[META_PIPE]; if (!pipePackets) pipePackets = old.meta[META_PIPE] = []; pipePackets[packet.pipeCount] = packet; filtered = false; break; } } } if (filtered) { const key = packet.timestamp + hash2; this._filteredPackets.unshift({ key, hash: hash2, packet, count: 1 }); } } }; // src/jdom/light.ts function cmdCode(cmd) { switch (cmd) { case "setall": return LIGHT_PROG_SET_ALL; case "fade": return LIGHT_PROG_FADE; case "fadehsv": return LIGHT_PROG_FADE_HSV; case "rotfwd": return LIGHT_PROG_ROTATE_FWD; case "rotback": return LIGHT_PROG_ROTATE_BACK; case "show": case "wait": return LIGHT_PROG_SHOW; case "range": return LIGHT_PROG_RANGE; case "mode": return LIGHT_PROG_MODE; case "tmpmode": return LIGHT_PROG_MODE1; case "setone": return LIGHT_PROG_COL1_SET; case "mult": return 256; default: return void 0; } } function isWhiteSpace(code) { return code == 32 || code == 13 || code == 10 || code == 9; } function lightEncode(format, args) { const outarr = []; let colors = []; let pos = 0; let currcmd = 0; function pushNumber(n) { if (n == null || (n | 0) != n || n < 0 || n >= 16383) throw new Error("number out of range: " + n); if (n < 128) outarr.push(n); else { outarr.push(128 | n >> 8); outarr.push(n & 255); } } function flush() { if (currcmd == 207) { if (colors.length != 1) throw new Error("setone requires 1 color"); } else { if (colors.length == 0) return; if (colors.length <= 3) outarr.push(192 | colors.length); else { outarr.push(192); outarr.push(colors.length); } } for (const c of colors) { outarr.push(c >> 16 & 255); outarr.push(c >> 8 & 255); outarr.push(c >> 0 & 255); } colors = []; } function nextToken() { while (isWhiteSpace(format.charCodeAt(pos))) pos++; const beg = pos; while (pos < format.length && !isWhiteSpace(format.charCodeAt(pos))) pos++; return format.slice(beg, pos); } while (pos < format.length) { const token = nextToken(); const t0 = token.charCodeAt(0); if (97 <= t0 && t0 <= 122) { flush(); currcmd = cmdCode(token); if (currcmd == void 0) throw new Error("unknown light command: " + token); if (currcmd == 256) { const f = parseFloat(nextToken()); if (isNaN(f) || f < 0 || f > 2) throw new Error("expecting scale"); outarr.push(216); outarr.push(3); outarr.push(208); const mm = Math.round(128 * f) & 255; outarr.push(193); outarr.push(mm); outarr.push(mm); outarr.push(mm); } else { outarr.push(currcmd); } } else if (48 <= t0 && t0 <= 57) { pushNumber(parseInt(token)); } else if (t0 == 37) { if (args.length == 0) throw new Error("out of args, %"); const v = args.shift(); if (typeof v != "number") throw new Error("expecting number"); pushNumber(v); } else if (t0 == 35) { if (token.length == 1) { if (args.length == 0) throw new Error("out of args, #"); const v = args.shift(); if (typeof v === "number") colors.push(v); else if (Array.isArray(v)) for (const vv of v) colors.push(vv); else throw Error("invalid number " + v); } else { if (token.length == 7) { const b = fromHex(token.slice(1)); const c = b[0] << 16 | b[1] << 8 | b[2]; colors.push(c); } else { throw new Error("invalid color: " + token); } } } } flush(); return new Uint8Array(outarr); } // src/jdom/setting.ts function localStorageSetting(key) { if (typeof self !== "undefined" && self.localStorage) { return { get: () => self.localStorage.getItem(key) ?? void 0, set: (v) => self.localStorage.setItem(key, v) }; } return void 0; } // src/jdom/nodesetting.ts var settingsPath = ""; function nodeSetting(key) { let v; let keyPath; const fs = require("fs"); function init() { if (!settingsPath) { const jd = process.env["HOME"] + "/.jacdac"; try { fs.mkdirSync(jd); } catch { } settingsPath = jd + "/settings"; try { fs.mkdirSync(settingsPath); } catch { } } if (!keyPath) { keyPath = settingsPath + "/" + key.replace(/[^a-z_\-]/g, (c) => c.charCodeAt(0) + ""); try { v = fs.readFileSync(keyPath, "utf8"); } catch { v = void 0; } } } function get() { init(); return v; } function set(nv) { init(); if (v !== nv) { v = nv; fs.writeFileSync(keyPath, nv, "utf8"); } } return { get, set }; } // src/jdom/servers/rolemanagerserver.ts var Role = class { constructor(name, classIdentifier) { this.name = name; this.classIdentifier = classIdentifier; } clear() { this.device = void 0; this.serviceIndex = void 0; } isAssigned() { return this.device != void 0; } assignment() { if (this.isAssigned()) return this.device.deviceId + ":" + this.serviceIndex; return void 0; } toString() { const binding = this.device ? this.device + ":" + this.serviceIndex : "?"; return `${this.name}[${binding}]`; } }; var RoleManagerServer = class extends JDServiceServer { constructor(bus, roleStore) { super(SRV_ROLE_MANAGER); this.bus = bus; this.roleStore = roleStore; this.roles = []; this.assignmentCache = this.read(); this.changed = debounce(this.changed.bind(this), 100); this.addCommand(129 /* SetRole */, this.handleSet.bind(this)); this.addCommand(131 /* ListRoles */, this.handleList.bind(this)); this.addCommand( 132 /* ClearAllRoles */, this.handleClearAssignments.bind(this) ); const alloc = this.addRegister(385 /* AllRolesAllocated */); alloc.on(REGISTER_PRE_GET, () => { const allDone = this.roles.every((r) => r.isAssigned()); alloc.setValues([allDone ? 1 : 0]); }); this.autoBindEnabled = this.addRegister(128 /* AutoBind */, [true]); this.initForBus(); } changed() { console.log("CHANGE: " + this.roles.join(", ")); this.emit(CHANGE); this.sendEvent(3 /* Change */); } initForBus() { for (const r of this.roles) { this.setFromCache(r); } this.bus.scheduler.setInterval(() => this.autoBind(), 980); this.bus.on(DEVICE_DISCONNECT, (dev) => { let numCleared = 0; for (const r of this.roles) { if (r.device == dev) { r.clear(); numCleared++; } } if (numCleared) this.changed(); }); } autoBind() { if (!this.autoBindEnabled.values()[0]) return; const usedBindings = {}; let numUnbound = 0; for (const r of this.roles) if (r.isAssigned()) usedBindings[r.assignment()] = true; else numUnbound++; if (numUnbound == 0) return; const devs = this.bus.devices(); devs.sort((a, b) => strcmp(a.deviceId, b.deviceId)); const assignedRoles = []; const roles = this.roles.slice(); roles.sort((a, b) => strcmp(a.name, b.name)); for (const r of roles) { if (!r.isAssigned()) for (const d of devs) { const len = d.serviceLength; for (let i = 1; i < len; ++i) { if (r.classIdentifier == d.serviceClassAt(i) && !usedBindings[d.deviceId + ":" + i]) { this.setRoleCore(r.name, d, i); usedBindings[r.assignment()] = true; assignedRoles.push(r); } } } } if (assignedRoles.length) { this.save(); this.changed(); } } setFromCache(r) { if (r.isAssigned()) return; const cached = this.assignmentCache[r.name]; if (typeof cached != "string") return; const [devId, idx_] = cached.split(":"); const dev = this.bus.device(devId, true); const idx = parseInt(idx_); if (!dev || !idx) return; if (dev.serviceClassAt(idx) != r.classIdentifier) return; this.setRole(r.name, dev, idx); } deleteRoles() { this.roles = []; this.changed(); } clearAssignments() { for (const r of this.roles) r.clear(); this.assignmentCache = {}; this.save(); this.changed(); } addRole(name, classIdenitifer) { let r = this.lookup(name); if (!r) { this.roles.push(r = new Role(name, classIdenitifer)); } else { if (r.classIdentifier == classIdenitifer) return; r.classIdentifier = classIdenitifer; if (r.device) { delete this.assignmentCache[r.name]; this.save(); } r.clear(); } this.setFromCache(r); this.changed(); } setRoleCore(name, dev, serviceIndex) { const r = this.lookup(name); if (r) { if (r.device == dev && r.serviceIndex == serviceIndex) return; for (const role of this.roles) { if (role.device == dev && role.serviceIndex == serviceIndex) { role.clear(); delete this.assignmentCache[role.name]; } } r.device = dev; r.serviceIndex = serviceIndex; this.assignmentCache[r.name] = r.assignment(); } } setRole(name, dev, serviceIndex) { this.setRoleCore(name, dev, serviceIndex); this.save(); this.changed(); } getRole(name) { const r = this.lookup(name); if (r) return { device: r.device, serviceIndex: r.serviceIndex }; else return void 0; } lookup(name) { return this.roles.find((r) => r.name == name); } async handleSet(pkt) { const [deviceId, serviceIdx, role] = pkt.jdunpack("b[8] u8 s"); const dev = this.bus.device(toHex2(deviceId), true, pkt); if (dev) this.setRole(role, dev, serviceIdx); } async handleList(pkt) { const pipe = OutPipe.from(this.bus, pkt, true); await pipe.respondForEach(this.roles, (r) => { const id = r.device ? fromHex(r.device.deviceId) : new Uint8Array(8); return jdpack( "b[8] u32 u8 s", [id, r.classIdentifier, r.serviceIndex || 0, r.name] ); }); } handleClearAssignments() { this.clearAssignments(); } read() { if (!this.roleStore) return {}; try { return JSON.parse(this.roleStore.get() || "{}"); } catch (e) { console.debug(e); return {}; } } save() { this.roleStore?.set(JSON.stringify(this.assignmentCache)); } }; // src/jdom/color.ts function hsvToCss(hue, saturation, value, brightness, monochrome) { const csshue = hue * 360 / 255; const csssat = (monochrome ? 255 : saturation) / 255; const cssval = value / 255; const [h, s, l] = hsv_to_hsl(csshue, csssat, cssval); const mixl = 0.3; const alpha = (mixl + (1 - mixl) * l) * brightness; return `hsla(${h}, ${s * 100}%, ${l * 100}%, ${alpha}`; } function hsv_to_hsl(h, s, v) { const l = (2 - s) * v / 2; if (l != 0) { if (l == 1) { s = 0; } else if (l < 0.5) { s = s * v / (l * 2); } else { s = s * v / (2 - l * 2); } } return [h, s, l]; } // src/jdom/bridge.ts var JDBridge = class extends JDClient { constructor(name, infrastructure = false) { super(); this.infrastructure = infrastructure; this.packetSent = 0; this.packetProcessed = 0; this.bridgeId = `bridge-${name}-` + randomDeviceId(); this.handleSendFrame = this.handleSendFrame.bind(this); } get bus() { return this._bus; } set bus(newBus) { if (newBus !== this._bus) { if (this._bus) this.unmount(); this._bus = newBus; if (this._bus) { this.mount( this._bus.subscribe(FRAME_PROCESS, this.handleSendFrame) ); this.mount(this._bus.addBridge(this)); } this.emit(CHANGE); } } /** * Decodes and distributes a payload * @param data */ receiveFrameOrPacket(data, sender) { const bus = this._bus; if (!bus) return; this.packetProcessed++; if (sender) data._jacdac_sender = sender; if (!data._jacdac_sender) data._jacdac_sender = this.bridgeId; this.currFrame = data; bus.sendFrameAsync(data); this.emit(FRAME_PROCESS, data); this.currFrame = null; } handleSendFrame(frame) { if (!this._bus || this.currFrame == frame || frame._jacdac_sender === this.bridgeId) return; this.packetSent++; this.sendPacket(frame, frame._jacdac_sender); } }; var ProxyBridge = class extends JDBridge { constructor(_sendPacket) { super("proxy", true); this._sendPacket = _sendPacket; } sendPacket(data, sender) { this._sendPacket(data, sender); } }; function createProxyBridge(sendPacket) { return new ProxyBridge(sendPacket); } // src/jdom/clients/settingsclient.ts var SettingsClient = class extends JDServiceClient { constructor(service) { super(service); service.registersUseAcks = true; } async clear() { await this.service.sendCmdAsync(133 /* Clear */); } async listKeys() { const inp = new InPipeReader(this.bus); await this.service.sendPacketAsync( inp.openCommand(130 /* ListKeys */), true ); const { output } = await inp.readAll(); const keys = output.map((pkt) => pkt.stringData); return keys.filter((k) => !!k); } async list() { const inp = new InPipeReader(this.bus); await this.service.sendPacketAsync( inp.openCommand(131 /* List */), true ); const { output } = await inp.readAll(); return output.map((pkt) => { const [key, value] = pkt.jdunpack("z b"); return key && { key, value }; }).filter((kv) => !!kv); } async setValue(key, value) { key = key.trim(); if (value === void 0) { await this.deleteValue(key); } else { const pkt = Packet.from( 129 /* Set */, jdpack("z b", [key, value]) ); await this.service.sendPacketAsync(pkt, true); this.emit(CHANGE); } } async setStringValue(key, value) { await this.setValue(key, value ? stringToBuffer(value) : void 0); } async getValue(key) { if (!key) return void 0; key = key.trim(); const pkt = Packet.from(128 /* Get */, jdpack("s", [key])); const resp = await this.service.sendCmdAwaitResponseAsync(pkt); const [rkey, value] = jdunpack(resp.data, "z b"); if (key !== rkey) { console.error( `device returned different key, got "${rkey}", expected "${key}"` ); return void 0; } return value; } async getStringValue(key) { const value = await this.getValue(key); return value && bufferToString(value); } async deleteValue(key) { if (!key) return; key = key.trim(); const pkt = Packet.from(132 /* Delete */, jdpack("s", [key])); await this.service.sendPacketAsync(pkt); this.emit(CHANGE); } }; // src/jdom/clients/devicescriptmanagerclient.ts var _DeviceScriptManagerClient = class extends JDServiceClient { constructor(service) { super(service); const changeEvent = service.event( 3 /* ProgramChange */ ); this.mount(changeEvent.subscribe(EVENT, () => this.emit(CHANGE))); this.mount( changeEvent.subscribe( EVENT, () => this.emit(_DeviceScriptManagerClient.PROGRAM_CHANGE) ) ); const panicEvent = service.event(128 /* ProgramPanic */); this.mount( panicEvent.subscribe( EVENT, (args) => this.emit( _DeviceScriptManagerClient.PROGRAM_PANIC, ...args || [] ) ) ); } deployBytecode(bytecode, onProgress) { return OutPipe.sendBytes( this.service, 128 /* DeployBytecode */, bytecode, onProgress ); } async setRunning(value) { const reg = this.service.register(128 /* Running */); await reg.sendSetAsync(jdpack("u8", [value ? 1 : 0])); } async setAutoStart(value) { const reg = this.service.register(129 /* Autostart */); await reg.sendSetAsync(jdpack("u8", [value ? 1 : 0])); } }; var DeviceScriptManagerClient = _DeviceScriptManagerClient; DeviceScriptManagerClient.PROGRAM_CHANGE = "programChange"; DeviceScriptManagerClient.PROGRAM_PANIC = "programPanic"; // src/jdom/transport/bluetooth.ts var JD_BLE_FIRST_CHUNK_FLAG = 128; function isWebBluetoothEnabled() { return !!Flags.webBluetooth; } function isWebBluetoothSupported() { try { return typeof navigator !== "undefined" && !!navigator.bluetooth && !!navigator.bluetooth.requestDevice; } catch (e) { return false; } } function bleRequestDevice(options) { if (!Flags.webBluetooth) return Promise.resolve(void 0); try { console.debug(`bluetooth request`, { options }); return navigator?.bluetooth?.requestDevice?.(options); } catch (e) { if (Flags.diagnostics) console.warn(e); return void 0; } } function bleGetDevices() { if (!Flags.webBluetooth) return Promise.resolve([]); try { return navigator?.bluetooth?.getDevices() || Promise.resolve([]); } catch (e) { if (Flags.diagnostics) console.warn(e); return Promise.resolve([]); } } var BluetoothTransport = class extends Transport { // eslint-disable-next-line @typescript-eslint/no-unused-vars constructor(options) { super(BLUETOOTH_TRANSPORT, { checkPulse: true }); this.handleDisconnected = this.handleDisconnected.bind(this); this.handleCharacteristicChanged = this.handleCharacteristicChanged.bind(this); } description() { return this._device?.name; } async transportConnectAsync(background) { if (background) { const devices = await bleGetDevices(); this._device = devices?.[0]; } else { const device = await bleRequestDevice({ filters: [{ namePrefix: "BBC micro:bit" }], optionalServices: [BLUETOOTH_JACDAC_SERVICE] }); this._device = device; } if (!this._device?.gatt) throw new Error("Device not found"); this._device.addEventListener( "gattserverdisconnected", this.handleDisconnected, false ); this._server = await this._device.gatt.connect(); this._service = await this._server.getPrimaryService( BLUETOOTH_JACDAC_SERVICE ); this._rxCharacteristic = await this._service.getCharacteristic( BLUETOOTH_JACDAC_RX_CHARACTERISTIC ); this._txCharacteristic = await this._service.getCharacteristic( BLUETOOTH_JACDAC_TX_CHARACTERISTIC ); this._rxCharacteristic.addEventListener( "characteristicvaluechanged", this.handleCharacteristicChanged, false ); await this._rxCharacteristic.startNotifications(); } async transportSendPacketAsync(data) { if (!this._txCharacteristic) { console.debug(`trying to send Bluetooth packet while disconnected`); return; } const length = data.length; const totalChunks = Math.ceil(data.length / 18); let remainingChunks = totalChunks == 0 ? 0 : totalChunks - 1; let sent = 0; while (sent < length) { const n = Math.min(18, length - sent); const chunk = data.slice(sent, sent + n); const header = new Uint8Array(2); header[0] = totalChunks & 127; if (sent == 0) header[0] |= JD_BLE_FIRST_CHUNK_FLAG; header[1] = remainingChunks; this._txCharacteristic.writeValueWithoutResponse( bufferConcat(header, chunk) ); sent += n; remainingChunks = remainingChunks == 0 ? 0 : remainingChunks - 1; console.debug( `chunk: ${chunk.toString()} [${remainingChunks} chunks remaining]` ); } } async transportDisconnectAsync() { if (!this._device) return; console.debug(`ble: disconnecting`); try { this._rxCharacteristic?.removeEventListener( "characteristicvaluechanged", this.handleCharacteristicChanged ); this._device?.removeEventListener( "gattserverdisconnected", this.handleDisconnected ); this._server.disconnect(); } finally { this._rxCharacteristic = void 0; this._txCharacteristic = void 0; this._service = void 0; this._server = void 0; this._device = void 0; this._rxBuffer = void 0; } } handleDisconnected() { this.disconnect(); } handleCharacteristicChanged() { const data = new Uint8Array(this._rxCharacteristic.value.buffer); const packetData = data.slice(2); console.debug(`received length ${data.length}`); if (data[0] & JD_BLE_FIRST_CHUNK_FLAG) { if (this._rxBuffer) console.error( `Dropped buffer. Chunks remaining: ${this._rxChunkCounter}` ); this._rxBuffer = new Uint8Array(); this._rxChunkCounter = data[0] & 127; console.debug(`Initial chunk counter: ${this._rxChunkCounter}`); } this._rxChunkCounter = this._rxChunkCounter == 0 ? 0 : this._rxChunkCounter - 1; console.debug( `after modification chunk counter: ${this._rxChunkCounter}` ); if (data[1] !== this._rxChunkCounter) console.error( `Data out of order. Expected chunk: ${this._rxChunkCounter} Got chunk: ${data[1]}` ); else this._rxBuffer = bufferConcat(this._rxBuffer, packetData); if (this._rxChunkCounter == 0) { this.bus.processFrame(this._rxBuffer, BLUETOOTH_TRANSPORT); this._rxBuffer = void 0; this._rxChunkCounter = 0; } } }; function createBluetoothTransport(options) { return isWebBluetoothSupported() && new BluetoothTransport(options); } // src/jdom/transport/hf2.ts var HF2_DEVICE_MAJOR = 42; var HF2_CMD_BININFO = 1; var HF2_MODE_BOOTLOADER = 1; var HF2_MODE_USERSPACE = 2; var HF2_CMD_INFO = 2; var HF2_CMD_RESET_INTO_APP = 3; var HF2_CMD_RESET_INTO_BOOTLOADER = 4; var HF2_CMD_START_FLASH = 5; var HF2_CMD_WRITE_FLASH_PAGE = 6; var HF2_CMD_CHKSUM_PAGES = 7; var HF2_CMD_READ_WORDS = 8; var HF2_CMD_WRITE_WORDS = 9; var HF2_CMD_DMESG = 16; var HF2_FLAG_SERIAL_OUT = 128; var HF2_FLAG_SERIAL_ERR = 192; var HF2_FLAG_CMDPKT_LAST = 64; var HF2_FLAG_CMDPKT_BODY = 0; var HF2_FLAG_MASK = 192; var HF2_SIZE_MASK = 63; var HF2_STATUS_OK = 0; var HF2_STATUS_INVALID_CMD = 1; var HF2_STATUS_EXEC_ERR = 2; var HF2_STATUS_EVENT = 128; var HF2_EV_MASK = 8388608; var HF2_CMD_JDS_CONFIG = 32; var HF2_CMD_JDS_SEND = 33; var HF2_EV_JDS_PACKET = 8388640; var HF2Proto = class { constructor(io) { this.io = io; this.eventHandlers = {}; this.msgs = new PromiseBuffer(); this.cmdSeq = Math.random() * 65535 | 0; this.lock = new PromiseQueue(); let frames = []; io.onData = (buf) => { const tp = buf[0] & HF2_FLAG_MASK; const len = buf[0] & 63; const frame = new Uint8Array(len); memcpy(frame, 0, buf, 1, len); if (tp & HF2_FLAG_SERIAL_OUT) { this.onSerial(frame, tp == HF2_FLAG_SERIAL_ERR); return; } frames.push(frame); if (tp == HF2_FLAG_CMDPKT_BODY) { return; } else { assert(tp == HF2_FLAG_CMDPKT_LAST); let total = 0; for (const f of frames) total += f.length; const r = new Uint8Array(total); let ptr = 0; for (const f of frames) { memcpy(r, ptr, f); ptr += f.length; } frames = []; if (r[2] & HF2_STATUS_EVENT) { this.handleEvent(r); } else { this.msgs.push(r); } } }; } error(m) { return this.io?.error(m); } talkAsync(cmd, data) { if (!this.io) throwError("hf2: rogue instance"); let len = 8; if (data) len += data.length; const pkt = new Uint8Array(len); const seq = ++this.cmdSeq & 65535; write32(pkt, 0, cmd); write16(pkt, 4, seq); write16(pkt, 6, 0); if (data) memcpy(pkt, 8, data, 0, data.length); let numSkipped = 0; const handleReturnAsync = () => this.msgs.shiftAsync(HF2_TIMEOUT).then((res) => { if (read16(res, 0) != seq) { if (numSkipped < 3) { numSkipped++; this.io.log( `message out of sync, (${seq} vs ${read16( res, 0 )}); will re-try` ); return handleReturnAsync(); } this.error("out of sync"); } let info = ""; if (res[3]) info = "; info=" + res[3]; switch (res[2]) { case HF2_STATUS_OK: return res.slice(4); case HF2_STATUS_INVALID_CMD: this.error("invalid command" + info); break; case HF2_STATUS_EXEC_ERR: this.error("execution error" + info); break; default: this.error("error " + res[2] + info); break; } return null; }).catch((e) => { console.debug(`hf2 error: ${e.message}; cmd=${cmd}`); if (this.io) { if (!isTimeoutError(e)) this.error(e); } return null; }); return this.enqueueTalk(async () => { if (!this.io) return null; return await this.sendMsgAsync(pkt).then(handleReturnAsync); }); } async enqueueTalk(talk) { try { if (!this.io) return void 0; return this.lock.enqueue("talk", talk); } catch (e) { if (!this.io) return; if (isCancelError(e)) return; throw e; } } sendMsgAsync(buf, serial = 0) { const frame = new Uint8Array(64); const loop = (pos) => { let len = buf.length - pos; if (len <= 0) return Promise.resolve(); if (len > 63) { len = 63; frame[0] = HF2_FLAG_CMDPKT_BODY; } else { frame[0] = HF2_FLAG_CMDPKT_LAST; } if (serial) frame[0] = serial == 1 ? HF2_FLAG_SERIAL_OUT : HF2_FLAG_SERIAL_ERR; frame[0] |= len; for (let i = 0; i < len; ++i) frame[i + 1] = buf[pos + i]; if (!this.io) return Promise.resolve(); return this.io.sendPacketAsync(frame).then(() => loop(pos + len)); }; return loop(0); } onEvent(id, f) { assert(!!(id & HF2_EV_MASK)); this.eventHandlers[id + ""] = f; } onJDMessage(f) { this.talkAsync(HF2_CMD_JDS_CONFIG, encodeU32LE([1])); this.onEvent(HF2_EV_JDS_PACKET, f); } sendJDMessageAsync(buf) { return this.talkAsync(HF2_CMD_JDS_SEND, buf).then(() => { }); } handleEvent(buf) { const evid = read32(buf, 0); const f = this.eventHandlers[evid + ""]; if (f) { f(buf.slice(4)); } else { this.io.log("unhandled event: " + evid.toString(16)); } } onSerial(data, iserr) { } async postConnectAsync() { await this.checkMode(); const buf = await this.talkAsync(HF2_CMD_INFO); this.io.log("connected to " + bufferToString(buf)); } async checkMode() { const info = await this.talkAsync(HF2_CMD_BININFO); if (!info) throwError("device disconnected"); const mode = read32(info, 0); this.io.log(`mode ${mode}`); if (mode == HF2_MODE_USERSPACE) { this.io.log(`device in user-space mode`); } else if (mode == HF2_MODE_BOOTLOADER) { this.io.log( `device in bootloader mode, reseting into user-space mode` ); await this.talkAsync(HF2_CMD_RESET_INTO_APP); throwError("Device in bootloader mode"); } else { throwError("Unknown device operation mode"); } } async disconnectAsync() { if (this.io) { const io = this.io; this.io = void 0; await io.disconnectAsync(); } } }; // src/jdom/transport/microbit.ts var MICROBIT_V2_VENDOR_ID = 3368; var MICROBIT_V2_PRODUCT_ID = 516; var pageShift = 10; var CMSISProto = class { constructor(io) { this.io = io; this.q = new PromiseQueue(); this.sendQ = []; } startRecvToLoop() { console.assert(!this._lastInterval); let last = this.recvTo; this._lastInterval = setInterval(() => { if (!this.io) this.stopRecvToLoop(); if (last && last == this.recvTo) { last(); } last = this.recvTo; }, 200); } stopRecvToLoop() { if (this._lastInterval) { clearInterval(this._lastInterval); this._lastInterval = void 0; } } error(msg, code) { this.stopRecvToLoop(); this.io?.error(msg, code); this.xchgAddr = null; } onJDMessage(f) { this._onJDMsg = f; } sendJDMessageAsync(buf) { if (buf.length & 3) { const tmp = new Uint8Array(buf.length + 3 & ~3); tmp.set(buf); buf = tmp; } return new Promise((resolve) => { this.sendQ.push({ buf, cb: resolve }); }); } async disconnectAsync() { if (this.io) { console.debug(`micro:bit: disconnect proto`); this.stopRecvToLoop(); this._onJDMsg = () => console.warn("rogue jd callback"); const io = this.io; this.io = void 0; if (io) await io.disconnectAsync(); } } recvAsync() { return new Promise((resolve, reject) => { if (!this.io) { reject(new Error("USB disconnected")); return; } this.io.recvPacketAsync().then( (v) => { const f = resolve; resolve = null; if (f) { this.recvTo = null; f(v); } }, (err) => { if (resolve) { resolve = null; this.recvTo = null; reject(err); } } ); this.recvTo = () => { if (resolve) { resolve = null; reject(new Error("CMSIS recv timeout")); } }; }); } talkAsync(cmds) { return this.q.enqueue("talk", async () => { if (!this.io) { this.error("micro:bit disconnected"); return; } await this.io.sendPacketAsync(new Uint8Array(cmds)); if (!this.io) { this.error("micro:bit disconnected"); return; } let response = await this.recvAsync(); if (response[0] !== cmds[0]) { const msg = `Bad response for ${cmds[0]} -> ${response[0]}, try again`; console.debug(msg, { cmds, response }); try { response = await this.recvAsync(); } catch (e) { this.error(msg); } if (response[0] !== cmds[0]) this.error(msg); } return response; }); } talkHexAsync(str) { return this.talkAsync(fromHex(str.replace(/ /g, ""))); } decodeString(buf) { const len = buf[1]; const ss = buf.slice(2, 2 + len); return uint8ArrayToString(ss); } async setBaudRate() { const setBaud = [130, 0, 0, 0, 0]; write32(setBaud, 1, 115200); await this.talkAsync(setBaud); } async xchgLoop() { let currSend; while (this.io) { const now = Date.now(); if (Flags.diagnostics && this.lastXchg && now - this.lastXchg > 50) { console.warn("slow xchg: " + (now - this.lastXchg) + "ms"); } this.lastXchg = now; let numev = 0; let inp = await this.readBytes(this.xchgAddr + 12, 256, true); if (inp[2]) { await this.writeWord(this.xchgAddr + 12, 0); await this.triggerIRQ(); inp = inp.slice(0, inp[2] + 12); this._onJDMsg(inp); numev++; } let sendFree = false; if (currSend) { const send = await this.readBytes(this.xchgAddr + 12 + 256, 4); if (!send[2]) { currSend.cb(); currSend = null; sendFree = true; numev++; } } if (!currSend && this.sendQ.length) { if (!sendFree) { const send = await this.readBytes( this.xchgAddr + 12 + 256, 4 ); if (!send[2]) sendFree = true; } if (sendFree) { currSend = this.sendQ.shift(); const bbody = currSend.buf.slice(4); await this.writeWords( this.xchgAddr + 12 + 256 + 4, new Uint32Array(bbody.buffer) ); const bhead = currSend.buf.slice(0, 4); await this.writeWords( this.xchgAddr + 12 + 256, new Uint32Array(bhead.buffer) ); await this.triggerIRQ(); this.lastSend = Date.now(); numev++; } else { if (this.lastSend) { const d = Date.now() - this.lastSend; if (d > 50) { this.lastSend = 0; console.warn("failed to send packet fast enough"); } } } } if (numev == 0) { await delay(0); } } } async readSerial() { let buf = await this.talkAsync([131]); const len = buf?.[1]; if (len) { buf = buf.slice(2, 2 + len); if (this.pendingSerial) buf = bufferConcat(this.pendingSerial, buf); let ptr = 0; let beg = 0; while (ptr < buf.length) { if (buf[ptr] == 10 || buf[ptr] == 13) { beg = ptr + 1; } ptr++; } buf = buf.slice(beg); this.pendingSerial = buf.length ? buf : null; if (this.pendingSerial) this.lastPendingSerial = Date.now(); } else if (this.pendingSerial) { const d = Date.now() - this.lastPendingSerial; if (d > 500) { const s = fromUTF8(uint8ArrayToString(this.pendingSerial)); this.pendingSerial = null; console.debug("SERIAL[TO]: " + s); } } return len; } async talkStringAsync(...cmds) { return this.talkAsync(cmds).then((buf) => this.decodeString(buf)); } async readDP(reg) { const nums = [5, 0, 1, 2 | reg, 0, 0, 0, 0]; const buf = await this.talkAsync(nums); return read32(buf, 3); } async setupTAR(addr) { const nums = [5, 0, 2, 1, 82, 0, 0, 35, 5, 0, 0, 0, 0]; write32(nums, 9, addr); await this.talkAsync(nums); } async writeWords(addr, data) { const pstart = addr >> pageShift; const pend = addr + data.length * 4 - 1 >> pageShift; if (pstart == pend) { return this.writeWordsCore(addr, data); } else { const addrend = (addr >> pageShift) + 1 << pageShift; const len = addrend - addr >> 2; await this.writeWords(addr, data.slice(0, len)); await this.writeWords(addrend, data.slice(len)); } } async writeWordsCore(addr, data) { await this.setupTAR(addr); const MAX = 14; let ptr = 0; const reqHd = [6, 0, MAX, 0, 13]; for (let i = 0; i < MAX * 4; ++i) reqHd.push(0); const req = new Uint8Array(reqHd); let overhang = 1; let ptrTX = 0; const count = data.length; const dataBytes = new Uint8Array(data.buffer); let lastCh = MAX; await this.q.enqueue("talk", async () => { while (ptr < count) { const ch = Math.min(count - ptrTX, MAX); if (ch) { req[2] = ch; req.set(dataBytes.slice(ptrTX * 4, (ptrTX + ch) * 4), 5); if (!this.io) { this.error("disconnected"); return; } await this.io.sendPacketAsync( ch == MAX ? req : req.slice(0, 5 + 4 * ch) ); ptrTX += ch; lastCh = ch; } if (overhang-- > 0) continue; const buf = await this.recvAsync(); if (buf[0] != req[0]) this.error(`bad response, ${buf[0]} != ${req[0]}`); if (buf[1] != MAX && buf[1] != lastCh) this.error( `bad response, ${buf[1]} != ${MAX} && ${buf[1]} != ${lastCh}` ); ptr += buf[1]; } }); } async readBytes(addr, count, jdmode = false) { if (addr & 3 || count & 3) this.error("unaligned"); const b = await this.readWords(addr, count >> 2, jdmode); return new Uint8Array(b.buffer); } async readWords(addr, count, jdmode = false) { const pstart = addr >> pageShift; const pend = addr + count * 4 - 1 >> pageShift; if (pstart == pend) { return this.readWordsCore(addr, count, jdmode); } else { const addrend = (addr >> pageShift) + 1 << pageShift; const len = addrend - addr >> 2; const b0 = await this.readWords(addr, len, jdmode); if (jdmode && b0.length < len) return b0; const b1 = await this.readWords(addrend, count - len); const res = new Uint32Array(count); res.set(b0); res.set(b1, len); return res; } } async readWordsCore(addr, count, jdmode) { await this.setupTAR(addr); const MAX = 14; const res = new Uint32Array(count); let ptr = 0; const req = new Uint8Array([6, 0, MAX, 0, 15]); let overhang = 1; let ptrTX = 0; let numPending = 0; await this.q.enqueue("talk", async () => { while (ptr < count || numPending) { const ch = Math.min(count - ptrTX, MAX); if (ch > 0) { req[2] = ch; numPending++; if (!this.io) { this.error("disconnected"); return; } await this.io.sendPacketAsync(req); ptrTX += ch; } if (overhang-- > 0) continue; const buf = await this.recvAsync(); numPending--; if (buf[0] != req[0]) { this.error("bad response"); return; } const len = buf[1]; const words = new Uint32Array( buf.slice(4, (1 + len) * 4).buffer ); if (words.length != len) { this.error("bad response2"); return; } res.set(words, ptr); if (jdmode && ptr == 0) { const frmsz = new Uint8Array(res.buffer)[2]; const words2 = frmsz + 12 + 3 >> 2; if (count > words2) count = words2; } ptr += words.length; } }); return res; } async findExchange() { const memStart = 536870912; const memStop = memStart + 128 * 1024; const checkSize = 1024; let p0 = 536895488; let p1 = 536895488 + checkSize; const check = async (addr) => { if (addr < memStart) return null; if (addr + checkSize > memStop) return null; const buf = await this.readWords(addr, checkSize >> 2); for (let i = 0; i < buf.length; ++i) { if (buf[i] == 2020426826 && buf[i + 1] == 2963718377) return addr + (i << 2); } return 0; }; while (true) { const a0 = await check(p0); if (a0) return a0; const a1 = await check(p1); if (a1) return a1; if (a0 === null && a1 === null) return null; p0 -= checkSize; p1 += checkSize; } } async triggerIRQ() { const addr = 3758154240 + (this.irqn >> 5) * 4; const data = new Uint32Array([1 << (this.irqn & 31)]); await this.writeWords(addr, data); } writeWord(addr, val) { return this.writeWords(addr, new Uint32Array([val])); } async reset() { await this.writeWord(3758157308, 0); await this.writeWord(3758157068, 100270080 | 1 << 2); } async postConnectAsync() { this.startRecvToLoop(); const devid = await this.talkStringAsync(128); if (/^9902/.test(devid)) { this.error( `micro:bit v1 is not supported. sorry.`, ERROR_MICROBIT_V1 ); return; } if (!/^990[3456789]/.test(devid)) { this.error( `Invalid Vendor0 response: ` + devid, ERROR_MICROBIT_UNKNOWN ); return; } this.io.log("DAPLink v" + await this.talkStringAsync(0, 4)); await this.setBaudRate(); const freq = [17, 0, 0, 0, 0]; write32(freq, 1, 1e7); await this.talkAsync(freq); const inits = [ "02 00", // connect "04 00 64 00 00 00", // configure delays // SWD switch "12 38 FF FF FF FF FF FF FF", // ones "12 10 9E E7", // SWD "12 38 FF FF FF FF FF FF FF", // ones "12 08 00", // zero // read DPIDR "05 00 01 02 00 00 00 00", // clear errors "05 00 03 00 04 00 00 00 08 00 00 00 00 04 00 00 00 50" ]; for (const ini of inits) await this.talkHexAsync(ini); for (let i = 0; i < 100; ++i) { const st = await this.readDP(4); const mask = 1 << 29 | 1 << 31; if ((st & mask) == mask) break; await delay(20); } await this.reset(); await delay(1e3); const xchg = await this.findExchange(); if (xchg === null) { this.error( `exchange address not found; add jacdac to your project`, ERROR_MICROBIT_JACDAC_MISSING ); return; } this.xchgAddr = xchg; const info = await this.readBytes(xchg, 16); this.irqn = info[8]; if (info[12 + 2] != 255) { this.error( "invalid memory; try power-cycling the micro:bit", ERROR_MICROBIT_INVALID_MEMORY ); return; } await this.writeWord(xchg + 12, 0); this.io.log( `exchange address: 0x${xchg.toString(16)}; irqn=${this.irqn}` ); this.xchgLoop().catch((e) => { if (!isCancelError(e)) console.debug(e); this.error(e?.message || "an error occured", errorCode(e)); }); } }; // src/jdom/transport/usbio.ts var USB_FILTERS = { filters: [ { // hf2 devices (incl. arcade) classCode: 255, subclassCode: HF2_DEVICE_MAJOR }, { // micro:bit v2 vendorId: MICROBIT_V2_VENDOR_ID, productId: MICROBIT_V2_PRODUCT_ID } ] }; var controlTransferGetReport = 1; var controlTransferSetReport = 9; var controlTransferOutReport = 512; var controlTransferInReport = 256; var USBIO = class { constructor(options) { this.options = options; this.readLoopStarted = false; this.ready = false; this.rawMode = false; // eslint-disable-next-line @typescript-eslint/no-unused-vars this.onData = (v) => { }; this.onError = (e) => { console.warn(`usb error: ${errorCode(e) || ""} ${e ? e.stack : e}`); }; } description() { return this.dev?.productName; } // eslint-disable-next-line @typescript-eslint/no-explicit-any log(msg, v) { if (Flags.diagnostics) { if (v != void 0) console.debug("usb: " + msg, v); else console.debug("usb: " + msg); } } mkProto() { return this.isMicrobit() ? new CMSISProto(this) : new HF2Proto(this); } clearDev() { if (this.dev) { this.dev = null; this.epIn = null; this.epOut = null; this.onData = () => console.warn("rogue hf2 onData"); } } disconnectAsync() { this.ready = false; if (!this.dev) return Promise.resolve(); console.debug("close device"); return this.dev.close().catch((e) => { if (!isCancelError(e)) console.debug(e); }).then(() => { this.clearDev(); return delay(500); }); } recvPacketAsync() { if (!this.rawMode) this.error("rawMode required"); return this.recvPacketCoreAsync(); } recvPacketCoreAsync() { const final = (res) => { if (res.status != "ok") this.error("USB IN transfer failed"); const arr = new Uint8Array(res.data.buffer); if (arr.length == 0) return this.recvPacketCoreAsync(); return arr; }; if (!this.dev) return Promise.reject(new Error("Disconnected")); if (!this.epIn) { return this.dev.controlTransferIn( { requestType: "class", recipient: "interface", request: controlTransferGetReport, value: controlTransferInReport, index: this.iface.interfaceNumber }, 64 ).then(final); } return this.dev.transferIn(this.epIn.endpointNumber, 64).then(final); } error(msg, code) { const e = new JDError( `device ${this.dev ? this.dev.productName : "n/a"} (${msg})`, { code } ); this.onError(e); } async readLoop() { if (this.rawMode || this.readLoopStarted) return; this.readLoopStarted = true; console.debug("usb: start read loop"); while (true) { if (!this.ready) { break; } try { const buf = await this.recvPacketCoreAsync(); if (buf[0]) { this.onData(buf); } else { await delay(5); } } catch (err) { if (this.dev) { if (!isCancelError(err)) this.onError(err); await this.disconnectAsync(); } await delay(300); } } } sendPacketAsync(pkt) { if (!this.dev) return Promise.reject(new Error("Disconnected")); assert(pkt.length <= 64); if (!this.epOut) { return this.dev.controlTransferOut( { requestType: "class", recipient: "interface", request: controlTransferSetReport, value: controlTransferOutReport, index: this.iface.interfaceNumber }, pkt ).then((res) => { if (res.status != "ok") this.error("USB CTRL OUT transfer failed"); }); } return this.dev.transferOut(this.epOut.endpointNumber, pkt).then((res) => { if (res.status != "ok") this.error("USB OUT transfer failed"); }); } isMicrobit() { return this.dev && this.dev.productId == 516 && this.dev.vendorId == 3368; } checkDevice() { this.iface = void 0; this.altIface = void 0; if (!this.dev) return false; console.debug( "usb: connect device " + this.dev.manufacturerName + " " + this.dev.productName ); const subcl = this.isMicrobit() ? 0 : HF2_DEVICE_MAJOR; for (const iface of this.dev.configuration.interfaces) { const alt = iface.alternates[0]; if (alt.interfaceClass == 255 && alt.interfaceSubclass == subcl) { this.iface = iface; this.altIface = alt; break; } } if (this.isMicrobit()) this.rawMode = true; return !!this.iface; } async tryReconnectAsync(deviceId) { try { const devices = await this.options.getDevices(USB_FILTERS); this.dev = deviceId ? devices.find((dev) => dev.serialNumber === deviceId) : devices[0]; } catch (e) { if (!isCancelError(e)) console.debug(e); this.dev = void 0; } } async requestDeviceAsync() { try { this.dev = await this.options.requestDevice(USB_FILTERS); } catch (e) { if (!isCancelError(e)) console.debug(e); this.dev = void 0; } } async connectAsync(background, deviceId) { await this.tryReconnectAsync(deviceId); if (!this.dev && !background) await this.requestDeviceAsync(); if (!this.dev && background) throwError("usb: device not paired", { cancel: true }); await this.openDeviceAsync(); const proto = this.mkProto(); try { await proto.postConnectAsync(); } catch (e) { if (!isCancelError(e)) console.debug(e); await proto.disconnectAsync(); throw e; } return proto; } async openDeviceAsync() { if (!this.dev) throwError("usb: device not found", { cancel: true }); if (!this.checkDevice()) throwError("usb: device does not support HF2", { code: ERROR_TRANSPORT_HF2_NOT_SUPPORTED }); await this.dev.open(); await this.dev.selectConfiguration(1); if (this.altIface.endpoints.length) { this.epIn = this.altIface.endpoints.filter( (e) => e.direction == "in" )[0]; this.epOut = this.altIface.endpoints.filter( (e) => e.direction == "out" )[0]; assert(this.epIn.packetSize == 64); assert(this.epOut.packetSize == 64); } console.debug(`usb: claim interface ${this.iface.interfaceNumber}`); await this.dev.claimInterface(this.iface.interfaceNumber); console.debug("usb: all connected"); this.ready = true; this.readLoop(); } }; // src/jdom/transport/jdusb.ts var JdUsbProto = class { constructor(io) { this.io = io; this.lock = new PromiseQueue(); this.frameHandler = (b) => { }; this.usb_rx_was_magic = 0; this.usb_rx_state = 0; this.usb_rx_ptr = 0; this.rxbuf = new Uint8Array(256); this.numFrames = 0; this.colorState = ""; io.isFreeFlowing = true; let acc = null; io.onData = (buf) => { if (acc) { buf = bufferConcat(acc, buf); acc = null; } if (this.hf2Resp) { this.io?.log(`read ${toHex2(buf.slice(0, 20))}...`); if (buf.length < this.hf2Resp.length) { acc = buf; return; } if (bufferEq( this.hf2Resp, buf.slice(1, 1 + this.hf2Resp.length) )) this.isHF2 = true; this.hf2Resp = null; } if (this.isHF2) return; this.decodeFrame(buf); }; } logError(msg) { console.error("JDUSB Error: " + msg); } handleProcessingFrame(fr) { const cmd = read16(fr, 14); this.io?.log("processing frame: 0x" + cmd.toString(16)); } handleFrame(fr) { this.numFrames++; const sz = fr[2] + 12; if (fr.length < 4 || fr.length < sz) { this.logError("short frm"); return; } fr = fr.slice(0, sz); const c = crc(fr.slice(2)); if (fr[0] != (c & 255) || fr[1] != c >> 8) { this.logError("crc err"); return; } if ((fr[3] & JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS) != 0 && read32(fr, 4) == SRV_USB_BRIDGE) { this.handleProcessingFrame(fr); } else { this.frameHandler(fr); } } decodeFrame(buf) { let serialBuf = []; const jd_usb_serial_cb = (c) => { serialBuf.push(c); }; const frame_error = () => { this.usb_rx_state = 0; for (let j = 0; j < this.usb_rx_ptr; ++j) { jd_usb_serial_cb(this.rxbuf[j]); } this.usb_rx_ptr = 0; }; for (let i = 0; i < buf.length; ++i) { let c = buf[i]; if (this.usb_rx_was_magic) { if (c == 254 /* Magic */) { if (this.usb_rx_state) { this.logError("dual magic"); frame_error(); continue; } } else { this.usb_rx_was_magic = 0; switch (c) { case 248 /* LiteralMagic */: c = 254 /* Magic */; break; case 252 /* FrameStart */: if (this.usb_rx_ptr) { this.logError("second begin"); frame_error(); } this.usb_rx_state = c; continue; case 253 /* FrameEnd */: if (this.usb_rx_state) { this.usb_rx_state = 0; const fr = this.rxbuf.slice(0, this.usb_rx_ptr); this.handleFrame(fr); this.usb_rx_ptr = 0; } else { this.logError("mismatched stop"); } continue; case 250 /* SerialGap */: if (serialBuf.length > 0) this.onSerial(new Uint8Array(serialBuf), false); serialBuf = []; this.onSerialGap(); continue; case 251 /* FrameGap */: this.onFrameGap(); continue; case 249 /* Reserved */: continue; default: if (this.usb_rx_state) { this.logError("invalid quote"); frame_error(); } jd_usb_serial_cb(254 /* Magic */); break; } } } else if (c == 254 /* Magic */) { this.usb_rx_was_magic = 1; continue; } if (this.usb_rx_state) { if (this.usb_rx_ptr >= this.rxbuf.length) { this.logError("frame ovf"); frame_error(); } else { this.rxbuf[this.usb_rx_ptr++] = c; } } else { jd_usb_serial_cb(c); } } if (serialBuf.length > 0) this.onSerial(new Uint8Array(serialBuf), false); } error(m) { return this.io?.error(m); } processingPkt(serviceCommand) { const f = jdpack("u16 u8 u8 u32 u32 u8 u8 u16", [ 0, // crc 4, // _size JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS | JD_FRAME_FLAG_COMMAND | JD_FRAME_FLAG_LOOPBACK, SRV_USB_BRIDGE, JD_DEVICE_IDENTIFIER_BROADCAST_HIGH_MARK, 0, // service size JD_SERVICE_INDEX_BROADCAST, serviceCommand ]); const c = crc(f.slice(2)); f[0] = c & 255; f[1] = c >> 8; return f; } encodeFrame(buf) { const c = crc(buf.slice(2)); if (buf[0] + (buf[1] << 8) != c) throw new Error("bad crc"); const outp = []; outp.push(254 /* Magic */); outp.push(252 /* FrameStart */); for (let ptr = 0; ptr < buf.length; ptr++) { const b = buf[ptr]; outp.push(b); if (b == 254 /* Magic */) outp.push(248 /* LiteralMagic */); } outp.push(254 /* Magic */); outp.push(253 /* FrameEnd */); const res = []; for (let i = 0; i < outp.length; i += 64) { res.push(new Uint8Array(outp.slice(i, i + 64))); } return res; } async detectHF2() { const pkt_en = this.encodeFrame( this.processingPkt(129 /* EnablePackets */) )[0]; const tag0 = 129; const tag1 = 66; const hf2_bininfo = new Uint8Array([ 72, 1, 0, 0, 0, tag0, tag1, 0, 0 ]); this.hf2Resp = new Uint8Array([tag0, tag1, 0, 0]); let frameToSend = hf2_bininfo; while (frameToSend.length + pkt_en.length < 64) { frameToSend = bufferConcat(frameToSend, pkt_en); } return this.enqueueTalk(async () => { for (let i = 0; i < 10; ++i) { this.io?.log(`detect hf2 ${i}...`); await this.io?.sendPacketAsync(frameToSend); await delay(200); if (!this.io) return; if (this.hf2Resp == null) { if (this.isHF2) { this.io.log("switching to HF2"); this.io.isFreeFlowing = false; return true; } else { this.io.log("detected JDUSB"); return false; } } } throwError( "JDUSB: can't connect, no HF2 nor JDUSB; port=" + (this.io.description?.() ?? ""), { code: ERROR_TRANSPORT_HF2_NOT_SUPPORTED } ); }); } async enqueueTalk(talk) { try { if (!this.io) return void 0; return this.lock.enqueue("talk", talk); } catch (e) { if (!this.io) return; if (isCancelError(e)) return; throw e; } } onJDMessage(f) { this.frameHandler = f; } async sendJDMessageAsync(fr) { this.enqueueTalk(async () => { for (const buf of this.encodeFrame(fr)) { if (!this.io) break; await this.io.sendPacketAsync(buf); } }); } fromUTF(line) { let str = uint8ArrayToString(line); try { str = fromUTF8(str); } catch { } return str; } flushSerial() { if (this.serialData) { console.debug("DEV-N: " + this.fromUTF(this.serialData)); this.serialData = null; } } onSerialGap() { this.flushSerial(); console.debug("DEV-S: [...some serial output skipped...]"); } onFrameGap() { console.debug("DEV-S: [...some Jacdac packets skipped...]"); } onSerial(data, iserr) { if (this.serialTimeout !== void 0) { clearTimeout(this.serialTimeout); this.serialTimeout = void 0; } let start = 0; for (let i = 0; i < data.length; ++i) { if (data[i] == 13 || data[i] == 10) { let line = data.slice(start, i); if (this.serialData) { line = bufferConcat(this.serialData, line); this.serialData = null; } if (line.length > 0) { const sline = this.fromUTF(line); const prevColor = this.colorState; const nocolors = sline.replace(/\x1B\[[0-9;]+m/, (f) => { this.colorState = f; return ""; }); const withcolors = prevColor + sline; this.onLog?.(nocolors, withcolors); console.debug("DEV: " + nocolors); } start = i + 1; } } if (start < data.length) { if (this.serialData) this.serialData = bufferConcat( this.serialData, data.slice(start) ); else this.serialData = data.slice(start); this.serialTimeout = setTimeout(() => { this.serialTimeout = void 0; this.flushSerial(); }, 300); } } async postConnectAsync() { for (let i = 0; i < 100; ++i) { await this.sendJDMessageAsync( this.processingPkt(129 /* EnablePackets */) ); await this.sendJDMessageAsync( this.processingPkt(131 /* EnableLog */) ); await delay(100); if (this.numFrames > 0) break; if (!this.io) break; this.io.log(`waiting for response ${i}...`); } this.io?.log("connected"); } async disconnectAsync() { if (this.io) { const io = this.io; this.io = void 0; await io.disconnectAsync(); } } }; // src/jdom/transport/webserialio.ts var WebSerialIO = class { constructor(bus) { this.bus = bus; this.readLoopStarted = false; this.ready = false; this.isFreeFlowing = true; // eslint-disable-next-line @typescript-eslint/no-unused-vars this.onData = (v) => { }; this.onError = (e) => { console.warn( `webserial error: ${errorCode(e) || ""} ${e ? e.stack : e}` ); }; } description() { const info = this.dev?.getInfo(); return info ? `vendor: ${info.usbVendorId}, product: ${info.usbProductId}` : void 0; } // eslint-disable-next-line @typescript-eslint/no-explicit-any log(msg, v) { if (Flags.diagnostics) { if (v != void 0) console.debug("webserial: " + msg, v); else console.debug("webserial: " + msg); } } clearDev() { if (this.dev) { this.dev = null; this.onData = () => console.warn("webserial: rogue hf2 onData"); } } disconnectAsync() { this.ready = false; if (!this.dev) return Promise.resolve(); console.debug("webserial: close device"); return this.cancelStreams().catch((e) => { if (!isCancelError(e)) console.debug(e); }).then(() => { this.clearDev(); return delay(500); }); } devInfo() { if (!this.dev) return "n/a"; const h = (n) => ("000" + n.toString(16)).slice(-4); const info = this.dev.getInfo(); return h(info.usbVendorId) + ":" + h(info.usbProductId); } error(msg, code) { const e = new JDError(`webserial: device ${this.devInfo()} (${msg})`, { code }); this.onError(e); } async cancelStreams() { if (this.reader) try { await this.reader.cancel(); this.reader.releaseLock(); } catch { } try { this.writer.releaseLock(); } catch { } await this.dev.close(); } async readLoop() { if (this.readLoopStarted) return; this.readLoopStarted = true; console.debug("webserial: start read loop"); const readpkt = async (reader) => { let value = null; for (; ; ) { const tmp = await reader.read(); if (tmp.done || !this.dev) return null; if (this.isFreeFlowing) return tmp.value; if (!value) value = tmp.value; else value = bufferConcat(value, tmp.value); if (value && (value.length & 63) == 0) return value; } }; for (; ; ) { const reader = this.dev?.readable?.getReader(); if (!reader) { await delay(100); continue; } this.reader = reader; console.debug("start new read loop round"); try { for (; ; ) { const value = await readpkt(reader); if (!value) break; if (!this.isFreeFlowing && value.length > 64) for (let i = 0; i < value.length; i += 64) { this.onData(value.slice(i, i + 64)); } else this.onData(value); } } catch (e) { if (this.dev) { if (!isCancelError(e)) this.onError(e); } await delay(100); } finally { try { reader.releaseLock(); } catch (e) { } await delay(100); } } } sendPacketAsync(pkt) { if (!this.dev || !this.writer) return Promise.reject(new Error("Disconnected")); assert(pkt.length <= 64); if (pkt.length < 64) { const p = pkt; pkt = new Uint8Array(64); pkt.set(p); } return this.writer.write(pkt); } async tryReconnectAsync() { const matchFilter = (port) => { const info = port?.getInfo(); const usbVendorId = info?.usbVendorId; return this.bus.deviceCatalog.matchVendorId("serial", usbVendorId); }; try { const ports = await navigator.serial.getPorts(); const filtered = ports.filter(matchFilter); this.dev = filtered[0]; } catch (e) { if (!isCancelError(e)) console.debug(e); this.dev = void 0; } } // eslint-disable-next-line @typescript-eslint/no-unused-vars async requestDeviceAsync(deviceId) { const vendorIds = this.bus.deviceCatalog.vendorIds("serial"); const options = Flags.developerMode ? {} : { filters: vendorIds.map((usbVendorId) => ({ usbVendorId })) }; try { console.debug("request serial", { options }); this.dev = await navigator.serial.requestPort(options); } catch (e) { if (!isCancelError(e)) console.debug(e); this.dev = void 0; } } async connectAsync(background, deviceId) { await this.tryReconnectAsync(); if (!this.dev && !background) await this.requestDeviceAsync(deviceId); if (!this.dev && background) throwError("device not paired", { cancel: true }); await this.openDeviceAsync(); const jdusb = new JdUsbProto(this); jdusb.onLog = (line) => { this.onLog?.(line); }; let proto = jdusb; try { const isHF2 = await jdusb.detectHF2(); if (isHF2) proto = new HF2Proto(this); await proto.postConnectAsync(); } catch (e) { if (!isCancelError(e)) console.debug(e); await proto.disconnectAsync(); throw e; } return proto; } async openDeviceAsync() { if (!this.dev) throwError("device not found", { cancel: true }); await this.dev.open({ baudRate: 15e5, bufferSize: 32 * 1024 }); this.writer = this.dev.writable?.getWriter(); if (!this.writer) throwError("no writer"); this.ready = true; this.readLoop(); } }; // src/jdom/transport/eventtargetobservable.ts var EventTargetObservable = class { constructor(element, eventName) { this.element = element; this.eventName = eventName; } subscribe(observer) { const handler = (ev) => !!observer.next && observer.next(ev); this.element.addEventListener(this.eventName, handler, false); return { unsubscribe: () => this.element.removeEventListener(this.eventName, handler) }; } }; // src/jdom/transport/usb.ts function isWebUSBEnabled() { return !!Flags.webUSB; } function isWebUSBSupported() { try { return typeof navigator !== "undefined" && !!navigator.usb && !!navigator.usb.getDevices; } catch (e) { return false; } } function usbRequestDevice(options) { if (!Flags.webUSB) return Promise.resolve(void 0); try { return navigator?.usb?.requestDevice?.(options); } catch (e) { if (Flags.diagnostics) console.warn(e); return void 0; } } function usbGetDevices() { if (!Flags.webUSB) return Promise.resolve([]); try { return navigator?.usb?.getDevices() || Promise.resolve([]); } catch (e) { if (Flags.diagnostics) console.warn(e); return Promise.resolve([]); } } var WebUSBTransport = class extends Transport { constructor(options) { super(USB_TRANSPORT, { ...options, checkPulse: true, disconnectOnError: true }); this.options = options; } description() { return this.transport?.description(); } async transportConnectAsync(background) { this.transport = new USBIO(this.options); this.transport.onError = (e) => this.errorHandler(USB_TRANSPORT, e); this.hf2 = await this.transport.connectAsync(background); this.hf2.onJDMessage(this.handleFrame.bind(this)); } async transportSendPacketAsync(buf) { if (!this.hf2) throw new Error("hf2 transport disconnected"); await this.hf2.sendJDMessageAsync(buf); } // eslint-disable-next-line @typescript-eslint/no-unused-vars async transportDisconnectAsync(background) { const h = this.hf2; this.hf2 = void 0; this.transport = void 0; if (h) await h.disconnectAsync(); } get pulseTimeout() { return 6e4; } }; function defaultOptions() { return isWebUSBSupported() && { getDevices: usbGetDevices, requestDevice: usbRequestDevice, connectObservable: new EventTargetObservable( navigator.usb, "connect" ), disconnectObservable: new EventTargetObservable( navigator.usb, "disconnect" ) }; } function createUSBTransport(options) { if (!options) options = defaultOptions(); return options && new WebUSBTransport(options); } function createUSBBus(usbOptions, options) { return new JDBus([createUSBTransport(usbOptions)], options); } // src/jdom/transport/workertransport.ts var WorkerTransport = class extends Transport { constructor(type, worker, options) { super(type, options); this.type = type; this.worker = worker; this.options = options; // eslint-disable-next-line @typescript-eslint/no-explicit-any this.pending = {}; this.worker.addEventListener("message", this.handleMessage.bind(this)); } // eslint-disable-next-line @typescript-eslint/no-explicit-any postMessageAsync(msg) { const id = msg.id = "" + Math.random(); const p = new Promise((resolve, reject) => { this.worker.postMessage(msg); this.pending[id] = { resolve, reject }; }); return p; } handleMessage(ev) { const data = ev.data; const { jacdac, type } = data || {}; if (!jacdac) return; switch (type) { case "packet": { const { payload } = data; this.handleFrame(payload); break; } case "frame": { const { payload } = data; this.handleFrame(payload); break; } case "connect": case "disconnect": { const { id, error } = data; const { resolve, reject } = this.pending[id] || {}; if (resolve) { delete this.pending[id]; if (error) reject(error); else resolve(void 0); } break; } case "error": { const { error, background, type: type2 } = data; if (Flags.diagnostics) console.debug(data); if (!background) this.errorHandler(type2, error); break; } } } async transportSendPacketAsync(buf) { this.worker.postMessage({ jacdac: true, type: "packet", payload: buf }); } async transportConnectAsync(background) { let deviceId; if (!background) { deviceId = await this.options.requestDevice(); } await this.postMessageAsync({ jacdac: true, type: "connect", deviceId, background }); } transportDisconnectAsync(background) { return this.postMessageAsync({ jacdac: true, type: "disconnect", background }); } }; function createUSBWorkerTransport(worker) { return isWebUSBEnabled() && new WorkerTransport(USB_TRANSPORT, worker, { checkPulse: true, requestDevice: () => usbRequestDevice(USB_FILTERS).then((dev) => dev?.serialNumber), connectObservable: new EventTargetObservable( navigator.usb, "connect" ), disconnectObservable: new EventTargetObservable( navigator.usb, "disconnect" ) }); } // src/jdom/transport/webserial.ts function isWebSerialEnabled() { return !!Flags.webSerial; } function isWebSerialSupported() { try { return typeof navigator !== "undefined" && !!navigator.serial && !!navigator.serial.getPorts; } catch (e) { return false; } } var WebSerialTransport = class extends Transport { constructor(options) { super(SERIAL_TRANSPORT, { ...options, checkPulse: true }); this.mkTransport = options.mkTransport; } description() { return this.transport?.description(); } async transportConnectAsync(background) { this.transport = this.mkTransport(this.bus); this.transport.onError = (e) => this.errorHandler(SERIAL_TRANSPORT, e); this.transport.onLog = (line) => this.handleLog(line); this.hf2 = await this.transport.connectAsync(background); this.hf2.onJDMessage(this.handleFrame.bind(this)); } async transportSendPacketAsync(buf) { if (!this.hf2) throw new Error("hf2 transport disconnected"); await this.hf2.sendJDMessageAsync(buf); } // eslint-disable-next-line @typescript-eslint/no-unused-vars async transportDisconnectAsync(background) { const h = this.hf2; this.hf2 = void 0; this.transport = void 0; if (h) await h.disconnectAsync(); } }; function defaultOptions2() { if (!isWebSerialSupported()) return void 0; const connectObservable = new EventTargetObservable( navigator.serial, "connect" ); const disconnectObservable = new EventTargetObservable( navigator.serial, "disconnect" ); return { mkTransport: (bus) => new WebSerialIO(bus), connectObservable, disconnectObservable }; } function createWebSerialTransport(options) { if (!options) options = defaultOptions2(); return options && new WebSerialTransport(options); } // src/jdom/bridges/iframebridge.ts var IFrameBridge = class extends JDBridge { constructor(targetOrigin) { super("iframe"); this.targetOrigin = targetOrigin; this.handleMessage = this.handleMessage.bind(this); window.addEventListener("message", this.handleMessage, false); this.mount( () => window.removeEventListener("message", this.handleMessage) ); } handleMessage(msg) { const { data } = msg; if (data.channel === "jacdac" && data.type === "messagepacket") { const payload = data.data; this.receiveFrameOrPacket(payload); } } sendPacket(data, sender) { const msg = { type: "messagepacket", channel: "jacdac", data, sender, broadcast: true }; window.parent.postMessage(msg, this.targetOrigin); } }; function createIFrameBridge(parentOrigin = "*") { return inIFrame() && new IFrameBridge(parentOrigin); } // src/jdom/transport/websockettransport.ts var RECONNECT_TIMEOUT = 5e3; function isWebSocketTransportSupported() { return typeof WebSocket !== "undefined" && typeof globalThis.Blob !== void 0; } var WebSocketTransport = class extends Transport { constructor(url, options) { super(WEBSOCKET_TRANSPORT, options); this.url = url; this.lastConnectTimestamp = 0; this.WebSocket = options?.WebSocket || WebSocket; this.protocols = options?.protocols; this.on(FRAME_SEND_DISCONNECT, this.handleSendDisconnect.bind(this)); } description() { return this.url; } handleSendDisconnect() { const now = this.bus.timestamp; if (now - this.lastConnectTimestamp > RECONNECT_TIMEOUT) { this.lastConnectTimestamp = now; this.connect(true); } } transportConnectAsync(background) { return new Promise((resolve, reject) => { this.ws = new this.WebSocket(this.url, this.protocols); if (this.ws.binaryType != "arraybuffer") this.ws.binaryType = "arraybuffer"; this.ws.onopen = () => { const f = resolve; resolve = null; if (f) f(); }; this.ws.onerror = (err) => { this.disconnect(background); if (resolve) { resolve = null; reject(err); } }; this.ws.onclose = () => this.disconnect(background); this.ws.onmessage = (ev) => { const { data } = ev; if (typeof data == "string") { const d = JSONTryParse(data, null); if (d) this.emit(SIDE_DATA, d); } else { const buffer = new Uint8Array(data); this.handleFrame(buffer); } }; }); } transportSendPacketAsync(data) { if (this.ws?.readyState === this.WebSocket.OPEN) { this.ws.send(data); } return Promise.resolve(); } sendSideData(data) { if (this.ws?.readyState === this.WebSocket.OPEN) { this.ws.send(JSON.stringify(data)); return Promise.resolve(); } else { throwError(`socket closed, can't send side data`, { code: ERROR_TRANSPORT_CLOSED }); } } transportDisconnectAsync(background) { try { this.ws?.close(); this.ws = void 0; } catch (e) { if (!background) throw e; } return Promise.resolve(); } toString() { return `websocket transport (state: ${this.ws?.readyState})`; } }; function createWebSocketTransport(url, options) { if (!isWebSocketTransportSupported()) throwError("WebSocket not supported", { code: ERROR_TRANSPORT_WEBSOCKET_NOT_SUPPORTED }); return new WebSocketTransport(url, options); } // src/jdom/transport/createbus.ts function createWebBus(options) { const { usbOptions, serialOptions, bluetoothOptions, iframeTargetOrigin, client = true, ...rest } = options || {}; const bus = new JDBus( [ usbOptions !== null ? createUSBTransport(usbOptions) : void 0, serialOptions !== null ? createWebSerialTransport(serialOptions) : void 0, bluetoothOptions !== null ? createBluetoothTransport(bluetoothOptions) : void 0 ], { client, ...rest } ); const iframeBridge = iframeTargetOrigin !== null && createIFrameBridge(iframeTargetOrigin); if (iframeBridge) iframeBridge.bus = bus; return bus; } function isWebTransportSupported() { return isWebUSBSupported() || isWebSerialSupported() || isWebBluetoothSupported(); } function createWebSocketBus(options) { const { url = "ws://127.0.0.1:8081/", webSocketOptions, busOptions = {} } = options || {}; const ws = createWebSocketTransport(url, webSocketOptions); const bus = new JDBus([ws], { disableRoleManager: true, client: false, ...busOptions }); bus.autoConnect = true; return bus; } // src/jdom/rolemanager.ts function escapeRoleName(role) { const m = /^(.+)(\?|\[)/.exec(role); const roleName = m?.[1] || role; return roleName; } var RoleManager = class extends JDClient { constructor(bus, configuration) { super(); this._roles = []; this.bus = bus; this.mount( this.bus.subscribe(DEVICE_ANNOUNCE, this.addServices.bind(this)) ); this.mount( this.bus.subscribe( DEVICE_DISCONNECT, this.removeServices.bind(this) ) ); this.updateRoles(configuration || []); } /** * Indicates if all roles are bound. */ get isBound() { return this._roles.every(({ service }) => !!service); } /** * Gets the list of roles tracked by the manager */ roles(bound = void 0) { if (bound !== void 0) return this._roles.filter(({ service }) => !!service === bound); else return this._roles.slice(0); } /** * Saves roles status * @returns */ saveRoles() { return this._roles.map(({ service, ...rest }) => ({ ...rest, serviceId: service?.id })); } get hash() { return JSON.stringify(this.saveRoles()); } /** * Updates the list of roles * @param newRoles */ updateRoles(newRoles) { const oldBound = this.isBound; const oldHash = this.hash; const supportedNewRoles = newRoles.filter( ({ serviceClass: serviceClass2 }) => serviceSpecificationFromClassIdentifier(serviceClass2) ); const preferreds = /* @__PURE__ */ new Set(); for (const role of supportedNewRoles.filter( (r) => !!r.preferredDeviceId )) { const key = role.preferredDeviceId + (role.preferredServiceIndex || -1); if (preferreds.has(key)) { role.preferredDeviceId = void 0; role.preferredServiceIndex = void 0; } else preferreds.add(key); } let i = 0; while (i < this._roles.length) { const role = this._roles[i]; if (!supportedNewRoles.find((r) => r.role === role.role)) { this._roles.splice(i, 1); this.emit(ROLE_UNBOUND, role.role); } else { i++; } } for (const newRole of supportedNewRoles) { const existingRole = this._roles.find((r) => r.role === newRole.role); if (!existingRole) { this._roles.push({ ...newRole }); } else { const bindingChanged = existingRole.serviceClass !== newRole.serviceClass || existingRole.preferredDeviceId != newRole.preferredDeviceId || existingRole.preferredServiceIndex != newRole.preferredServiceIndex; existingRole.serviceClass = newRole.serviceClass; existingRole.preferredDeviceId = newRole.preferredDeviceId; existingRole.preferredServiceIndex = newRole.preferredServiceIndex; if (existingRole.service && bindingChanged) { existingRole.service = void 0; this.emit(ROLE_UNBOUND, existingRole.role); } } if (newRole.preferredDeviceId) { const otherBinding = this._roles.find( (r) => r.service && r.service.device.deviceId === newRole.preferredDeviceId && (isNaN(newRole.preferredServiceIndex) || r.service.serviceIndex === newRole.preferredServiceIndex) ); if (otherBinding) { otherBinding.service = void 0; this.emit(ROLE_UNBOUND, otherBinding.role); } } } const changed = oldHash !== this.hash; this.bindServices(changed); this.emitBoundEvents(oldBound); } /** * Resolves the service bound to a given role. * @param role * @returns */ service(role) { return this._roles.find((r) => r.role === role)?.service; } /** * Updates or creates a new role * @param role name of the role * @param serviceClass desired service class * @param preferredDeviceId optional preferred device id * @returns */ updateRole(role, serviceClass2, preferredDeviceId, preferredServiceIndex) { const newRoles = this._roles.slice(0).map((r) => ({ ...r })); let binding = newRoles.find((r) => r.role === role); if (binding) { binding.service = void 0; binding.preferredDeviceId = preferredDeviceId; binding.preferredServiceIndex = preferredServiceIndex; } else { binding = { role, serviceClass: serviceClass2, preferredDeviceId, preferredServiceIndex }; newRoles.push(binding); } if (preferredDeviceId) { const other = this._roles.find( (r) => r !== binding && r.preferredDeviceId === preferredDeviceId && r.preferredServiceIndex === preferredServiceIndex ); if (other) { other.preferredDeviceId = void 0; other.preferredServiceIndex = void 0; } } this.updateRoles(newRoles); } emitBoundEvents(oldBound) { const bound = this.isBound; if (oldBound !== bound) this.emit(bound ? BOUND : UNBOUND); } // TODO: need to respect other (unbound) role's preferredDeviceId bindRole(role) { const bound = this.roles(true); const unboundServices = this.bus.services({ ignoreInfrastructure: true, serviceClass: role.serviceClass }).filter((s) => !bound.find((r) => r.service === s)); const boundServices = bound.map((r) => r.service).filter((srv) => !!srv); let theOne = unboundServices[0]; if (role.preferredDeviceId) { const newOne = [...unboundServices, ...boundServices].find( (s) => s.device.deviceId === role.preferredDeviceId && (isNaN(role.preferredServiceIndex) || role.preferredServiceIndex === s.serviceIndex) ); if (newOne) { theOne = newOne; } } if (theOne) { role.service = theOne; this.emit(ROLE_BOUND, role.role); return true; } else return false; } bindServices(changed) { const r = this.roles().sort((l, r2) => { let c = 0; if (r2.preferredDeviceId || l.preferredDeviceId) c = -(l.preferredDeviceId || "").localeCompare( r2.preferredDeviceId || "" ); if (c != 0) return c; if (!isNaN(l.preferredServiceIndex) || !isNaN(r2.preferredServiceIndex)) c = -(l.preferredServiceIndex || 0) + (r2.preferredServiceIndex || 0); return c; }); r.forEach((binding) => { if (this.bindRole(binding)) changed = true; }); if (changed) this.emit(CHANGE); } addServices(dev) { if (dev === this.bus.selfDevice) return; this.bindServices(); } removeServices(dev) { let changed = false; this._roles.filter((r) => r.service?.device === dev).forEach((r) => { r.service = void 0; this.emit(ROLE_UNBOUND, r.role); changed = true; }); this.bindServices(changed); } toString() { return this._roles.map(({ role, service }) => `${role}->${service || "?"}`).join(","); } }; function startRoles(bus, bindings, onUpdate, options) { const { incomplete } = options || {}; const roleManager = new RoleManager(bus); roleManager.updateRoles( Object.keys(bindings).map((role) => ({ role, serviceClass: bindings[role].serviceClass, preferredDeviceId: bindings[role].preferredDeviceId, preferredServiceIndex: bindings[role].preferredServiceIndex })) ); const roles = () => { const r = {}; for (const key in bindings) { const srv = roleManager.service(key); if (srv) r[key] = srv; } return r; }; const update = () => { if (!incomplete && !roleManager.isBound) return; onUpdate(roles()); }; const unsubscribe = roleManager.subscribe(CHANGE, update); update(); return unsubscribe; } // src/jdom/sensors.ts var _sensorSpecs; function sensorSpecifications() { if (!_sensorSpecs) { _sensorSpecs = serviceSpecifications().filter( (srv) => !srv.shortName.startsWith("_") && isSensor(srv) ); } return _sensorSpecs; } function snapshotSensors(bus, sparse) { const r = toMap( sensorSpecifications(), (srv) => srv.camelName, (srv) => { const r2 = bus.services({ serviceClass: srv.classIdentifier, ignoreInfrastructure: true, announced: true }).map((srv2) => { const reg = srv2.readingRegister; const spec = reg.specification; return spec.fields.length === 1 ? reg.unpackedValue?.[0] || 0 : reg.objectValue || {}; }); return sparse && !r2.length ? void 0 : r2; }, sparse ); return r; } // src/jdom/bridges/websocketbridge.ts var WebSocketBridge = class extends JDBridge { constructor(name, url, protocols) { super(name, true); this.url = url; this.protocols = protocols; this.mount(() => this.close()); } close() { console.debug(`web bridge closed`, { url: this.url }); const opened = !!this._ws || !!this._startPromise; try { this._ws?.close(); this._ws = void 0; this._startPromise = void 0; } catch (e) { console.warn(e); } if (opened) this.emit(CLOSE); } async connect() { if (this._ws) return Promise.resolve(); if (!this._startPromise) { this._startPromise = new Promise((resolve, reject) => { const ws = new WebSocket(this.url, this.protocols); ws.binaryType = "arraybuffer"; ws.onopen = () => { this._ws = ws; this._startPromise = void 0; console.debug(`web bridge opened`, { url: this.url }); resolve(); }; ws.onerror = (e) => { console.debug(`web bridge error`, { url: this.url }); this.close(); reject(); }; ws.onclose = (ev) => { console.debug(`web bridge onclose`, { ev }); this.close(); }; ws.onmessage = (ev) => { const { data } = ev; const buffer = new Uint8Array(data); this.receiveFrameOrPacket(buffer); }; }); } return this._startPromise; } sendPacket(data, sender) { this.connect(); this._ws?.send(data); } }; // src/jdom/transport/nodesocket.ts var NodeSocketTransport = class extends Transport { constructor(port = 8082, host = "127.0.0.1", options) { super(NODESOCKET_TRANSPORT, options); this.port = port; this.host = host; } description() { return `${this.host}:${this.port}`; } transportConnectAsync(background) { return new Promise((resolve) => { const net = require("net"); this.sock = net.createConnection(this.port, this.host, resolve); this.sock.on("error", (e) => { if (!background) console.error(e); this.disconnect(background); }); this.sock.on("end", () => this.disconnect(background)); this.sock.setNoDelay(); let acc; this.sock.on("data", (buf) => { if (acc) { buf = bufferConcat(acc, buf); acc = null; } else { buf = new Uint8Array(buf); } while (buf) { const endp = buf[0] + 1; if (buf.length >= endp) { const pkt = buf.slice(1, endp); if (buf.length > endp) buf = buf.slice(endp); else buf = null; this.handleFrame(pkt); } else { acc = buf; buf = null; } } }); }); } transportSendPacketAsync(data) { const buf = new Uint8Array(1 + data.length); buf[0] = data.length; buf.set(data, 1); this.sock.write(buf); return Promise.resolve(); } transportDisconnectAsync(background) { try { this.sock?.end(); this.sock = void 0; } catch (e) { if (!background) throw e; } return Promise.resolve(); } toString() { return `socket transport (local port: ${this.sock?.localPort})`; } }; function createNodeSocketTransport(port, host, options) { return new NodeSocketTransport(port, host, options); } // src/jdom/transport/nodespi.ts var debug2 = (msg) => { }; var XFER_SIZE = 256; var SPI_TRANSFER_ATTEMPT_COUNT = 10; var SpiTransport = class extends Transport { constructor(rpio, spi, options) { super("spi", options); this.rpio = rpio; this.spi = spi; this.options = options; this.sendQueue = []; this.receiveQueue = []; this.handleRxPinRising = this.handleRxPinRising.bind(this); this.handleTxPinRising = this.handleTxPinRising.bind(this); this.rpio.init({ gpiomem: true, mapping: "physical" }); } async transportConnectAsync(background) { try { await this.internalTransportConnectAsync(); } catch (e) { console.debug(e); console.error("SPI configuration failed: make sure to install rpio"); this.disconnectRpio(); throw e; } } async internalTransportConnectAsync() { debug2("spi: connecting..."); const { txReadyPin, rxReadyPin, resetPin } = this.options; const { HIGH, LOW, POLL_HIGH, PULL_DOWN, INPUT, OUTPUT } = this.rpio; debug2("spi: setup pins"); this.rpio.open(txReadyPin, INPUT, PULL_DOWN); this.rpio.open(rxReadyPin, INPUT, PULL_DOWN); this.rpio.open(resetPin, OUTPUT); debug2("spi: reset bridge"); this.rpio.write(resetPin, LOW); await this.bus.delay(10); this.rpio.write(resetPin, HIGH); this.rpio.mode(resetPin, INPUT); debug2("spi: connect spi"); this.spiDevice = this.spi.openSync(0, 0); this.rpio.poll(rxReadyPin, this.handleRxPinRising, POLL_HIGH); this.rpio.poll(txReadyPin, this.handleTxPinRising, POLL_HIGH); debug2("spi: ready"); await this.transfer(); } async transportDisconnectAsync(background) { this.disconnectRpio(); } disconnectRpio() { try { const { txReadyPin, rxReadyPin, resetPin } = this.options; this.rpio.close(txReadyPin); this.rpio.close(rxReadyPin); this.rpio.close(resetPin); this.spiDevice?.closeSync(); this.spiDevice = void 0; } catch (e) { console.debug(e); } } handleRxPinRising() { this.transfer(); } handleTxPinRising() { this.transfer(); } async transportSendPacketAsync(p) { this.sendQueue.push(p); this.transfer(); } async transfer() { let todo = true; while (todo) { todo = await this.transferFrame(); while (this.receiveQueue.length > 0) { const frame = this.receiveQueue.shift(); this.handleFrame(frame, true); } } } async transferFrame() { const { txReadyPin, rxReadyPin } = this.options; const { HIGH } = this.rpio; const txReady = this.rpio.read(txReadyPin) == HIGH; const rxReady = this.rpio.read(rxReadyPin) == HIGH; const sendtx = this.sendQueue.length > 0 && txReady; if (!sendtx && !rxReady) return false; const txqueue = new Uint8Array(XFER_SIZE); const rxqueue = new Uint8Array(txqueue.length); let txq_ptr = 0; while (this.sendQueue.length > 0 && txq_ptr + this.sendQueue[0].length < XFER_SIZE) { const pkt = this.sendQueue.shift(); txqueue.set(pkt, txq_ptr); txq_ptr += pkt.length + 3 & ~3; } if (txq_ptr == 0 && !rxReady) return false; const ok = await this.attemptTransferBuffers(txqueue, rxqueue); if (!ok) { debug2("transfer failed"); return false; } if (rxReady) { let framep = 0; while (framep + 4 < XFER_SIZE) { const frame2 = rxqueue[framep + 2]; if (frame2 == 0) break; let sz = frame2 + 12; if (framep + sz > XFER_SIZE) { debug2(`packet overflow ${framep} + ${sz} > ${XFER_SIZE}`); break; } const frame0 = rxqueue[framep]; const frame1 = rxqueue[framep + 1]; const frame3 = rxqueue[framep + 3]; if (frame0 == 255 && frame1 == 255 && frame3 == 255) { } else { const computed = crc(rxqueue.slice(framep + 2, framep + sz)); const actual = read16(rxqueue, framep); if (computed != actual) { debug2(`invalid crc ${computed} != ${actual}`); break; } const frame = rxqueue.slice(framep, framep + sz); this.receiveQueue.push(frame); } sz = sz + 3 & ~3; framep += sz; } } return true; } async attemptTransferBuffers(txqueue, rxqueue) { for (let i = 0; i < SPI_TRANSFER_ATTEMPT_COUNT; i++) { try { const msg = { sendBuffer: Buffer.from(txqueue), receiveBuffer: Buffer.alloc(rxqueue.length), byteLength: txqueue.length, speedHz: 156e5 }; this.spiDevice.transferSync([msg]); msg.receiveBuffer.copy(rxqueue, 0, 0, rxqueue.length); return true; } catch (ex) { debug2(ex); await this.bus.delay(1); } } return false; } }; var RPI_PIN_TX_READY = 18; var RPI_PIN_RX_READY = 22; var RPI_PIN_RST = 15; var RPI_SPI_BUS_ID = 0; function createNodeSPITransport(rpio, spi, options) { if (!options) { options = { txReadyPin: RPI_PIN_TX_READY, rxReadyPin: RPI_PIN_RX_READY, resetPin: RPI_PIN_RST, spiBusId: RPI_SPI_BUS_ID }; } return new SpiTransport(rpio, spi, options); } // src/jdom/transport/nodewebserialio.ts var SCAN_INTERVAL = 2500; function toPromise(f) { return new Promise( (resolve, reject) => f((err, result) => { if (err) reject(err); else resolve(result); }) ); } async function listPorts(bus, serialPort) { const ports = await serialPort.list(); return ports.filter( (p) => /^(PX|JD)/.test(p.serialNumber) || bus.deviceCatalog.matchVendorId("serial", parseInt(p.vendorId, 16)) ); } async function cancelStreams(dev) { await toPromise((cb) => { if (!dev.isOpen) cb(void 0, void 0); else dev.close(cb); }); } var NodeWebSerialIO = class { /** * * @param SerialPort ``require("serialport")`` */ // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(bus, SerialPort) { this.bus = bus; this.SerialPort = SerialPort; this.isFreeFlowing = true; // eslint-disable-next-line @typescript-eslint/no-unused-vars this.onData = (v) => { }; this.onError = (e) => { console.warn(`serial error: ${errorCode(e) || ""} ${e ? e.stack : e}`); }; } // eslint-disable-next-line @typescript-eslint/no-explicit-any log(msg, v) { if (Flags.diagnostics) { if (v != void 0) console.debug("serial: " + msg, v); else console.debug("serial: " + msg); } } clearDev() { if (this.dev) { this.dev = null; this.port = null; } } disconnectAsync() { const d = this.dev; if (!d) return Promise.resolve(); this.clearDev(); return cancelStreams(d).catch((e) => { if (!isCancelError(e)) console.debug(e); }).then(() => delay(500)); } description() { return this.port ? `${this.port.path}, vendor: ${this.port.vendorId}, product: ${this.port.productId}` : void 0; } devInfo() { if (!this.port) return "n/a"; return this.port.vendorId + ":" + this.port.productId; } error(msg, code) { const e = new JDError(`serial device ${this.devInfo()} (${msg})`, { code }); this.onError(e); } sendPacketAsync(pkt) { if (!this.dev) { return Promise.resolve(); } assert(pkt.length <= 64); if (pkt.length < 64) { const p = pkt; pkt = new Uint8Array(64); pkt.set(p); } return toPromise((cb) => this.dev.write(pkt, void 0, cb)).catch( (e) => { this.error("write error: " + e.message); } ); } async tryReconnectAsync(deviceId) { try { this.dev = void 0; this.port = void 0; const ports = await listPorts(this.bus, this.SerialPort); this.port = ports?.[0]; if (this.port) { console.debug(`serial: found ${this.port.serialNumber}`); await toPromise((cb) => { this.dev = new this.SerialPort( { path: this.port.path, baudRate: 15e5 }, cb ); }); let tmpdata; this.dev.on("data", (buf) => { if (this.isFreeFlowing) { this.onData(buf); return; } if (tmpdata) buf = bufferConcat(tmpdata, buf); tmpdata = null; if (buf.length & 63) { tmpdata = buf; return; } if (buf.length > 64) for (let i = 0; i < buf.length; i += 64) { this.onData(buf.slice(i, i + 64)); } else this.onData(buf); }); this.dev.on("error", (err) => { this.error(err.messsage || err + ""); }); this.dev.on("close", () => this.disconnectAsync()); } } catch (e) { if (!isCancelError(e)) console.debug(e); this.dev = void 0; this.port = void 0; } } async connectAsync(background, deviceId) { await this.tryReconnectAsync(deviceId); if (!this.dev && background) throwError("can't find suitable device", { cancel: true }); if (!this.dev) throwError("device not found", { cancel: true }); console.debug(`serial: found ${this.devInfo()}`); const jdusb = new JdUsbProto(this); let proto = jdusb; try { const isHF2 = await jdusb.detectHF2(); if (isHF2) proto = new HF2Proto(this); await proto.postConnectAsync(); } catch (e) { if (!isCancelError(e)) console.debug(e); await proto.disconnectAsync(); throw e; } return proto; } }; var SerialPortWatch = class extends JDEventSource { constructor(SerialPort) { super(); this.SerialPort = SerialPort; this.watch(); } watch() { let knownPortIds = []; const interval = setInterval(async () => { if (!this.bus) return; const ports = await listPorts(this.bus, this.SerialPort); const portIds = ports.map((port) => port.serialNumber || port.path); const added = portIds.filter((id) => knownPortIds.indexOf(id) < 0); const removed = knownPortIds.filter((id) => portIds.indexOf(id) < 0); if (added.length || removed.length) console.debug( `detected serial port change + ${added.join( ", " )} - ${removed.join(", ")}` ); knownPortIds = portIds; if (added.length) this.emit(CONNECT); if (removed.length) this.emit(DISCONNECT); }, SCAN_INTERVAL); return { unsubscribe: () => clearInterval(interval) }; } }; function createNodeWebSerialTransport(SerialPort) { const watch = new SerialPortWatch(SerialPort); const connectObservable = { subscribe: (observer) => ({ unsubscribe: watch.subscribe(CONNECT, observer.next) }) }; const disconnectObservable = { subscribe: (observer) => ({ unsubscribe: watch.subscribe(DISCONNECT, observer.next) }) }; return new WebSerialTransport({ mkTransport: (bus) => { watch.bus = bus; return new NodeWebSerialIO(bus, SerialPort); }, connectObservable, disconnectObservable }); } // src/jdom/transport/nodewebusb.ts function createNodeUSBOptions(WebUSB) { console.debug(`jacdac: creating usb transport`); async function devicesFound(devices) { for (const device of devices) { const { vendorId, productId, deviceVersionMajor } = device; if (vendorId === MICROBIT_V2_VENDOR_ID && productId === MICROBIT_V2_PRODUCT_ID) { console.debug(`usb: found micro:bit v2`); return device; } else if (deviceVersionMajor == HF2_DEVICE_MAJOR) { for (const iface of device.configuration.interfaces) { const alt = iface.alternates[0]; if (alt.interfaceClass == 255 && alt.interfaceSubclass == HF2_DEVICE_MAJOR) { return device; } } } } return void 0; } const usb = new WebUSB({ devicesFound, allowAllDevices: true }); async function requestDevice(options) { console.debug(`usb: requesting device...`); try { const device = await usb.requestDevice(options); return device; } catch (e) { if (!isCancelError(e)) console.debug(e); return void 0; } } async function getDevices(options) { const dev = await requestDevice(options); return dev ? [dev] : []; } const connectObservable = new EventTargetObservable(usb, "connect"); const disconnectObservable = new EventTargetObservable(usb, "disconnect"); return { getDevices, requestDevice, connectObservable, disconnectObservable }; } // src/jdom/devtools.ts function startDevTools() { if (typeof window !== "undefined" && !/^https:\/\/microsoft.github.io\/jacdac-docs\//.test( window.location.href )) window.location.href = `https://microsoft.github.io/jacdac-docs/clients/javascript/devtools.html#${window.location.href}`; } function injectDevTools(bus, options) { if (typeof window === "undefined" || document.getElementById("jacdac-dev-tools")) return void 0; if (!document.getElementById("jacdac-dev-tools-style")) { const style = document.createElement("style"); style.id = "jacdac-dev-tools-style"; style.innerText = ` #jacdac-dev-tools { position: fixed; overflow: hide; box-shadow: 4px 4px 4px 4px #ccc; width: 40rem; height: min(48rem, 64%); background: #fff; z-index: 1000000; transition: left 0.5s, right 0.5s, bottom 0.5s, top 0.5s, width 0.5s, height 0.5s, opacity 1s; left:2rem; bottom: 2rem; } #jacdac-dev-tools button { float: right; margin-right: 0.5rem; } #jacdac-dev-tools.right { left: calc(100% - 42rem); } #jacdac-dev-tools.tall { height: calc(100% - 4rem); } #jacdac-dev-tools.shallow { height: max(22rem, 30%); } #jacdac-dev-tools > .header { font-size: 0.8rem; font-family: monospace; margin: 0.2rem; height: 1.5rem; } #jacdac-dev-tools > iframe { height: calc(100% - 1.5rem); width: 100%; border: none; } `; document.head.appendChild(style); } const { dashboardUrl = "https://microsoft.github.io/jacdac-docs/dashboard/" } = options || {}; const frameid = randomDeviceId(); const container = document.createElement("div"); container.id = "jacdac-dev-tools"; container.classList.add("right"); const header = document.createElement("div"); header.className = "header"; container.append(header); const iframe = document.createElement("iframe"); iframe.allow = "gamepad; microphone; camera; accelerometer; gyroscope; ambient-light-sensor; magnetometer"; iframe.sandbox.add( "allow-forms", "allow-downloads-without-user-activation", "allow-downloads", "allow-popups", "allow-popups-to-escape-sandbox", "allow-same-origin", "allow-scripts" ); iframe.src = `${dashboardUrl}?embed=1&connect=0&transient=1#${frameid}`; container.append(iframe); document.body.insertBefore(container, document.body.firstElementChild); const unsub = bus.addBridge( createProxyBridge((data, sender) => { iframe.contentWindow?.postMessage({ type: "messagepacket", channel: "jacdac", data, sender }); }) ); const cleanup = () => { unsub?.(); container.remove(); }; const addButton = (text, onclick) => { const btn = document.createElement("button"); btn.innerText = text; btn.onclick = () => onclick(btn); header.append(btn); }; addButton("close", cleanup); addButton(">>>", () => container.classList.add("right")); const up = "^"; const mid = "-"; const low = "."; addButton(up, (btn) => { if (btn.innerText === up) { btn.innerText = low; container.classList.remove("shallow"); container.classList.add("tall"); } else if (btn.innerText === low) { btn.innerText = mid; container.classList.add("shallow"); container.classList.remove("tall"); } else { btn.innerText = up; container.classList.remove("shallow"); container.classList.remove("tall"); } }); addButton("<<<", () => container.classList.remove("right")); return cleanup; } // src/jdom/semver.ts function cmp(a, b) { if (!a) if (!b) return 0; else return 1; else if (!b) return -1; else { let d = a.major - b.major || a.minor - b.minor || a.patch - b.patch; if (d) return d; if (a.pre.length == 0 && b.pre.length > 0) return 1; if (a.pre.length > 0 && b.pre.length == 0) return -1; for (let i = 0; i < a.pre.length + 1; ++i) { const aa = a.pre[i]; const bb = b.pre[i]; if (!aa) if (!bb) return 0; else return -1; else if (!bb) return 1; else if (/^\d+$/.test(aa)) if (/^\d+$/.test(bb)) { d = parseInt(aa) - parseInt(bb); if (d) return d; } else return -1; else if (/^\d+$/.test(bb)) return 1; else { d = strcmp2(aa, bb); if (d) return d; } } return 0; } } function versionTryParse(v) { if (!v) return null; if ("*" === v) { return { major: Number.MAX_SAFE_INTEGER, minor: Number.MAX_SAFE_INTEGER, patch: Number.MAX_SAFE_INTEGER, pre: [], build: [] }; } if (/^v\d/i.test(v)) v = v.slice(1); const m = /^(\d+)\.(\d+)\.(\d+)(-([0-9a-zA-Z\-\.]+))?(\+([0-9a-zA-Z\-\.]+))?$/.exec( v ); if (m) return { major: parseInt(m[1]), minor: parseInt(m[2]), patch: parseInt(m[3]), pre: m[5] ? m[5].split(".") : [], build: m[7] ? m[7].split(".") : [] }; return null; } function strcmp2(a, b) { if (a === b) return 0; if (a < b) return -1; else return 1; } function semverCmp(a, b) { const aa = versionTryParse(a); const bb = versionTryParse(b); if (!aa && !bb) return strcmp2(a, b); else return cmp(aa, bb); } // src/servers/gamepadservermanager.ts var GamepadHostManager = class extends JDClient { constructor(bus) { super(); this.bus = bus; this.providers = []; this.ticking = false; this.handleGamepadConnected = this.handleGamepadConnected.bind(this); this.handleGamepadDisconnected = this.handleGamepadDisconnected.bind(this); this.mount(this.removeEventListener.bind(this)); this.addEventListeners(); } static start(bus) { if (typeof window !== "undefined" && window.navigator && window.navigator.getGamepads) { return new GamepadHostManager(bus); } return void 0; } addEventListeners() { if (typeof window === "undefined") return; window.addEventListener( "gamepadconnected", this.handleGamepadConnected, false ); window.addEventListener( "gamepaddisconnected", this.handleGamepadDisconnected, false ); } removeEventListener() { if (typeof window === "undefined") return; window.removeEventListener( "gamepadconnected", this.handleGamepadConnected ); window.removeEventListener( "gamepaddisconnected", this.handleGamepadDisconnected ); } // eslint-disable-next-line @typescript-eslint/no-unused-vars handleGamepadConnected(event) { console.debug("gamepad connected"); if (!this.ticking) this.tick(); } handleGamepadDisconnected(event) { console.debug("gamepad disconnected"); const { gamepad } = event; const { index } = gamepad; const provider = this.providers[index]; if (provider) { this.bus.removeServiceProvider(provider.deviceProvider); this.providers[index] = void 0; } if (!this.ticking) this.tick(); } pollGamepads() { try { const r = navigator.getGamepads(); return r; } catch (e) { return void 0; } } update() { const gamepads = this.pollGamepads(); const now = this.bus.timestamp; for (let i = 0; i < gamepads?.length; ++i) { const gamepad = gamepads[i]; if (!gamepad) continue; let host = this.providers[i]; if (!host) { const service = new GamepadServer({ variant: 4 /* Gamepad */, buttonsAvailable: GAMEPAD_ARCADE_BUTTONS }); const deviceHost = new JDServerServiceProvider("gamepad", [ service ]); this.bus.addServiceProvider(deviceHost); this.providers[i] = host = { service, deviceProvider: deviceHost, timestamp: now }; } host.timestamp = now; host.service.update(gamepad); } } tick() { this.ticking = true; this.update(); if (this.providers.some((h) => h !== void 0)) window.requestAnimationFrame(() => this.tick()); else this.ticking = false; } }; // src/servers/loggerserver.ts var LoggerServer = class extends JDServiceServer { constructor() { super(SRV_LOGGER); this.minPriority = this.addRegister(128 /* MinPriority */, [ 4 /* Silent */ ]); } async report(priority, msg) { const pkt = Packet.jdpacked(priority, "s", [msg]); await this.sendPacketAsync(pkt); } }; // src/servers/verifiedtelemetryserver.ts var VerifiedTelemetryServer = class extends JDServiceServer { constructor(options) { super(SRV_VERIFIED_TELEMETRY, options); const { fingerprintType = 1 /* FallCurve */, telemetryStatusInterval = 5e3 } = options || {}; this.telemetryStatus = this.addRegister( 384 /* TelemetryStatus */, [1 /* Working */] ); this.telemetryStatusInterval = this.addRegister( 128 /* TelemetryStatusInterval */, [telemetryStatusInterval] ); this.fingerprintType = this.addRegister(385 /* FingerprintType */, [fingerprintType]); this.fingerprintTemplate = this.addRegister( 386 /* FingerprintTemplate */, [50, new Uint8Array(0)] ); this.addCommand( 128 /* ResetFingerprintTemplate */, this.handleResetTelemetryTemplate.bind(this) ); this.addCommand( 129 /* RetrainFingerprintTemplate */, this.handleRetrainTelemetryTemplate.bind(this) ); this.telemetryStatus.on( CHANGE, () => this.sendEvent( 3 /* TelemetryStatusChange */, this.telemetryStatus.data ) ); this.fingerprintTemplate.on( CHANGE, () => this.sendEvent(128 /* FingerprintTemplateChange */) ); } handleResetTelemetryTemplate() { this.fingerprintTemplate.setValues([50, new Uint8Array(0)]); } handleRetrainTelemetryTemplate() { this.fingerprintTemplate.setValues([50, new Uint8Array(0)]); } }; // src/servers/devicescriptmanagerserver.ts var _DeviceScriptManagerServer = class extends JDServiceServer { constructor() { super(SRV_DEVICE_SCRIPT_MANAGER); this._binary = new Uint8Array(0); this.running = this.addRegister(128 /* Running */, [false]); this.autoStart = this.addRegister(129 /* Autostart */, [ true ]); this.programSize = this.addRegister( 384 /* ProgramSize */, [this._binary.length] ); this.programHash = this.addRegister( 385 /* ProgramHash */, [fnv1(this._binary)] ); this.addCommand( 128 /* DeployBytecode */, this.handleDeployBytecode.bind(this) ); this.addCommand( 129 /* ReadBytecode */, this.handleReadBytecode.bind(this) ); } get binary() { return this._binary; } get debugInfo() { return this._debugInfo; } setBytecode(binary, debugInfo) { binary = binary || new Uint8Array(0); const [hash2] = this.programHash.values(); const valueHash = fnv1(binary); if (hash2 !== valueHash) { this._binary = binary; this._debugInfo = debugInfo; this.programSize.setValues([binary.length]); this.programHash.setValues([valueHash]); this.emit(_DeviceScriptManagerServer.PROGRAM_CHANGE); this.emit(CHANGE); this.sendEvent(3 /* ProgramChange */); } } handleDeployBytecode(pkt) { console.debug(`devicescript server: deploy`, { pkt }); } async handleReadBytecode(pkt) { const pipe = OutPipe.from(this.device.bus, pkt, true); await pipe.sendBytes(this._binary); await pipe.close(); } }; var DeviceScriptManagerServer = _DeviceScriptManagerServer; DeviceScriptManagerServer.PROGRAM_CHANGE = "programChange"; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ACK_MAX_DELAY, ACK_MIN_DELAY, ADVERTISE, ALIGN, ANNOUNCE, AccelerometerEvent, AccelerometerReg, AccelerometerRegPack, AcidityReg, AcidityRegPack, AirPressureReg, AirPressureRegPack, AirQualityIndexReg, AirQualityIndexRegPack, AnalogSensorServer, ArcadeGamepadButton, ArcadeGamepadEvent, ArcadeGamepadEventPack, ArcadeGamepadReg, ArcadeGamepadRegPack, ArcadeSoundCmd, ArcadeSoundCmdPack, ArcadeSoundReg, ArcadeSoundRegPack, BLUETOOTH_JACDAC_DIAG_CHARACTERISTIC, BLUETOOTH_JACDAC_RX_CHARACTERISTIC, BLUETOOTH_JACDAC_SERVICE, BLUETOOTH_JACDAC_TX_CHARACTERISTIC, BLUETOOTH_TRANSPORT, BOUND, BUS_NODE_NAME, BarcodeReaderEvent, BarcodeReaderEventPack, BarcodeReaderFormat, BarcodeReaderReg, BarcodeReaderRegPack, BaseCmd, BaseCmdPack, BaseEvent, BaseEventPack, BaseReg, BaseRegPack, BitRadioCmd, BitRadioCmdPack, BitRadioReg, BitRadioRegPack, BootloaderCmd, BootloaderCmdPack, BootloaderError, BrailleDisplayReg, BrailleDisplayRegPack, BrailleDisplayServer, BridgeReg, BridgeRegPack, BusInteractionMode, ButtonEvent, ButtonEventPack, ButtonReg, ButtonRegPack, ButtonServer, BuzzerCmd, BuzzerCmdPack, BuzzerReg, BuzzerRegPack, BuzzerServer, CHANGE, CLOSE, CLOUD_COMMAND, CMD_ADVERTISEMENT_DATA, CMD_EVENT_CODE_MASK, CMD_EVENT_COUNTER_MASK, CMD_EVENT_COUNTER_POS, CMD_EVENT_MASK, CMD_GET_REG, CMD_REG_MASK, CMD_SET_REG, CMD_TOP_MASK, CMSISProto, COMMAND_NODE_NAME, COMMAND_RECEIVE, CONNECT, CONNECTING, CONNECTION_STATE, CONST_LED_MAX_PIXELS_LENGTH, CONST_NODE_NAME, CONST_SYSTEM_ANNOUNCE_INTERVAL, CRC_ACK_NODE_NAME, CapacitiveButtonCmd, CapacitiveButtonReg, CapacitiveButtonRegPack, CharacterScreenReg, CharacterScreenRegPack, CharacterScreenServer, CharacterScreenTextDirection, CharacterScreenVariant, CloudAdapterCmd, CloudAdapterCmdPack, CloudAdapterEvent, CloudAdapterEventPack, CloudAdapterReg, CloudAdapterRegPack, CloudAdapterServer, CloudConfigurationCmd, CloudConfigurationCmdPack, CloudConfigurationConnectionStatus, CloudConfigurationEvent, CloudConfigurationEventPack, CloudConfigurationReg, CloudConfigurationRegPack, CodalMessageBusCmd, CodalMessageBusCmdPack, CodalMessageBusEvent, CodalMessageBusEventPack, ColorReg, ColorRegPack, CompassCmd, CompassReg, CompassRegPack, CompassServer, ConnectionState, ControlAnnounceFlags, ControlCmd, ControlCmdPack, ControlPipe, ControlPipePack, ControlReg, ControlRegPack, ControlServer, DATA, DEVICE_ANNOUNCE, DEVICE_CHANGE, DEVICE_CLEAN, DEVICE_CONNECT, DEVICE_DISCONNECT, DEVICE_FIRMWARE_INFO, DEVICE_FOUND, DEVICE_IMAGE_HEIGHT, DEVICE_IMAGE_WIDTH, DEVICE_LOST, DEVICE_NODE_NAME, DEVICE_PACKET_ANNOUNCE, DEVICE_RESTART, DISCONNECT, DISCONNECTING, DISPOSE, DOCS_ROOT, DcCurrentMeasurementReg, DcCurrentMeasurementRegPack, DcVoltageMeasurementReg, DcVoltageMeasurementRegPack, DcVoltageMeasurementVoltageMeasurementType, DeviceCatalog, DeviceScriptConditionCmd, DeviceScriptConditionEvent, DeviceScriptManagerClient, DeviceScriptManagerCmd, DeviceScriptManagerCmdPack, DeviceScriptManagerEvent, DeviceScriptManagerEventPack, DeviceScriptManagerPipe, DeviceScriptManagerPipePack, DeviceScriptManagerReg, DeviceScriptManagerRegPack, DeviceScriptManagerServer, DeviceStatsMonitor, DevsDbgCmd, DevsDbgCmdPack, DevsDbgEvent, DevsDbgEventPack, DevsDbgFiberHandle, DevsDbgFunIdx, DevsDbgObjStackFrame, DevsDbgPipe, DevsDbgPipePack, DevsDbgProgramCounter, DevsDbgReg, DevsDbgRegPack, DevsDbgStepFlags, DevsDbgString, DevsDbgSuspensionType, DevsDbgValueSpecial, DevsDbgValueTag, DistanceReg, DistanceRegPack, DistanceVariant, DmxCmd, DmxCmdPack, DmxReg, DmxRegPack, DotMatrixReg, DotMatrixRegPack, DotMatrixServer, DotMatrixVariant, DualMotorsReg, DualMotorsRegPack, DualMotorsServer, ECO2Reg, ECO2RegPack, ECO2Variant, EMBED_MIN_ASPECT_RATIO, ERROR, ERROR_MICROBIT_INVALID_MEMORY, ERROR_MICROBIT_JACDAC_MISSING, ERROR_MICROBIT_UNKNOWN, ERROR_MICROBIT_V1, ERROR_NO_ACK, ERROR_TIMEOUT, ERROR_TRANSPORT_CLOSED, ERROR_TRANSPORT_DEVICE_LOCKED, ERROR_TRANSPORT_HF2_NOT_SUPPORTED, ERROR_TRANSPORT_WEBSOCKET_NOT_SUPPORTED, EVENT, EVENT_NODE_NAME, FIELD_NODE_NAME, FIRMWARE_BLOBS_CHANGE, FLASH_MAX_DEVICES, FOUND, FRAME_PROCESS, FRAME_PROCESS_LARGE, FRAME_SEND, FRAME_SEND_DISCONNECT, FirmwareUpdater, Flags, FlexReg, FlexRegPack, GAMEPAD_ARCADE_BUTTONS, GAMEPAD_DPAD_AB_BUTTONS, GAMEPAD_DPAD_A_BUTTONS, GAMEPAD_DPAD_BUTTONS, GAMEPAD_DPAD_XY_BUTTONS, GAMEPAD_GAMEPAD_EXTRA_BUTTONS, GET_ATTEMPT, GLOBALS_UPDATED, GPIOCapabilities, GPIOCmd, GPIOCmdPack, GPIOMode, GPIOReg, GPIORegPack, GamepadButtons, GamepadEvent, GamepadEventPack, GamepadHostManager, GamepadReg, GamepadRegPack, GamepadServer, GamepadVariant, GyroscopeReg, GyroscopeRegPack, HF2Proto, HF2_CMD_BININFO, HF2_CMD_CHKSUM_PAGES, HF2_CMD_DMESG, HF2_CMD_INFO, HF2_CMD_JDS_CONFIG, HF2_CMD_JDS_SEND, HF2_CMD_READ_WORDS, HF2_CMD_RESET_INTO_APP, HF2_CMD_RESET_INTO_BOOTLOADER, HF2_CMD_START_FLASH, HF2_CMD_WRITE_FLASH_PAGE, HF2_CMD_WRITE_WORDS, HF2_DEVICE_MAJOR, HF2_EV_JDS_PACKET, HF2_EV_MASK, HF2_FLAG_CMDPKT_BODY, HF2_FLAG_CMDPKT_LAST, HF2_FLAG_MASK, HF2_FLAG_SERIAL_ERR, HF2_FLAG_SERIAL_OUT, HF2_MODE_BOOTLOADER, HF2_MODE_USERSPACE, HF2_SIZE_MASK, HF2_STATUS_EVENT, HF2_STATUS_EXEC_ERR, HF2_STATUS_INVALID_CMD, HF2_STATUS_OK, HF2_TIMEOUT, HIDJoystickServer, HIDKeyboardServer, HIDMouseServer, HeartRateReg, HeartRateRegPack, HeartRateVariant, HidJoystickCmd, HidJoystickCmdPack, HidJoystickReg, HidJoystickRegPack, HidKeyboardAction, HidKeyboardCmd, HidKeyboardCmdPack, HidKeyboardModifiers, HidKeyboardSelector, HidMouseButton, HidMouseButtonEvent, HidMouseCmd, HidMouseCmdPack, HumidityReg, HumidityRegPack, I2CCmd, I2CCmdPack, I2CReg, I2CRegPack, I2CStatus, IDENTIFY, IDENTIFY_DURATION, IlluminanceReg, IlluminanceRegPack, InPipe, InPipeReader, IndexedScreenCmd, IndexedScreenCmdPack, IndexedScreenReg, IndexedScreenRegPack, IndexedScreenServer, JACDAC_ERROR, JDBridge, JDBus, JDCancellationToken, JDClient, JDDevice, JDError, JDEvent, JDEventSource, JDField, JDNode, JDRegister, JDRegisterServer, JDServerServiceProvider, JDService, JDServiceClient, JDServiceMemberNode, JDServiceProvider, JDServiceServer, JDSubscriptionScope, JD_ADVERTISEMENT_0_ACK_SUPPORTED, JD_ADVERTISEMENT_0_COUNTER_MASK, JD_DEVICE_DISCONNECTED_DELAY, JD_DEVICE_IDENTIFIER_BROADCAST_HIGH_MARK, JD_DEVICE_LOST_DELAY, JD_FRAME_FLAG_ACK_REQUESTED, JD_FRAME_FLAG_COMMAND, JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS, JD_FRAME_FLAG_LOOPBACK, JD_FRAME_FLAG_VNEXT, JD_SERIAL_HEADER_SIZE, JD_SERIAL_MAX_PAYLOAD_SIZE, JD_SERVICE_INDEX_BROADCAST, JD_SERVICE_INDEX_CRC_ACK, JD_SERVICE_INDEX_CTRL, JD_SERVICE_INDEX_INV_MASK, JD_SERVICE_INDEX_MASK, JD_SERVICE_INDEX_MAX_NORMAL, JD_SERVICE_INDEX_PIPE, JSONTryParse, KeyboardClientEvent, KeyboardClientEventPack, LATE, LOG, LOST, LedReg, LedRegPack, LedServer, LedSingleCmd, LedSingleCmdPack, LedSingleReg, LedSingleRegPack, LedSingleVariant, LedStripCmd, LedStripCmdPack, LedStripLightType, LedStripReg, LedStripRegPack, LedStripServer, LedStripVariant, LedVariant, LevelDetector, LightBulbReg, LightBulbRegPack, LightLevelReg, LightLevelRegPack, LightLevelVariant, LoggerCmd, LoggerCmdPack, LoggerPriority, LoggerReg, LoggerRegPack, LoggerServer, MAX_SERVICES_LENGTH, MESSAGE, META_ACK, META_ACK_FAILED, META_GET, META_NOT_IMPLEMENTED, META_PIPE, META_TRACE, META_TRACE_DESCRIPTION, MICROBIT_V2_PRODUCT_ID, MICROBIT_V2_VENDOR_ID, MagneticFieldLevelEvent, MagneticFieldLevelReg, MagneticFieldLevelRegPack, MagneticFieldLevelServer, MagneticFieldLevelVariant, MagnetometerCmd, MagnetometerReg, MagnetometerRegPack, MatrixKeypadEvent, MatrixKeypadEventPack, MatrixKeypadReg, MatrixKeypadRegPack, MatrixKeypadServer, MatrixKeypadVariant, MicrophoneCmd, MicrophoneCmdPack, MicrophoneReg, MicrophoneRegPack, MidiOutputCmd, MidiOutputCmdPack, MidiOutputReg, MidiOutputRegPack, ModelRunnerCmd, ModelRunnerCmdPack, ModelRunnerModelFormat, ModelRunnerReg, ModelRunnerRegPack, MotionEvent, MotionReg, MotionRegPack, MotionVariant, MotorReg, MotorRegPack, MotorServer, MultitouchEvent, MultitouchEventPack, MultitouchReg, MultitouchRegPack, NEW_LISTENER, NODESOCKET_TRANSPORT, NumberFormat, OutPipe, PACKETIO_TRANSPORT, PACKET_ANNOUNCE, PACKET_DATA_NORMALIZE, PACKET_EVENT, PACKET_INVALID_CRC, PACKET_INVALID_DATA, PACKET_KIND_ANNOUNCE, PACKET_KIND_EVENT, PACKET_KIND_RO, PACKET_KIND_RW, PACKET_PRE_PROCESS, PACKET_PROCESS, PACKET_RECEIVE, PACKET_RECEIVE_ANNOUNCE, PACKET_RECEIVE_NO_DEVICE, PACKET_REPORT, PACKET_SEND, PANIC, PCControllerCmd, PCControllerCmdPack, PCControllerServer, PCMonitorReg, PCMonitorRegPack, PCMonitorServer, PING_LOGGERS_POLL, PIPE_CLOSE_MASK, PIPE_COUNTER_MASK, PIPE_METADATA_MASK, PIPE_NODE_NAME, PIPE_PORT_SHIFT, PIPE_REPORT_NODE_NAME, PROGRESS, PUBLISH, Packet, PlanarPositionReg, PlanarPositionRegPack, PlanarPositionServer, PlanarPositionVariant, PotentiometerReg, PotentiometerRegPack, PotentiometerVariant, PowerCmd, PowerEvent, PowerEventPack, PowerPowerStatus, PowerReg, PowerRegPack, PowerServer, PowerSupplyReg, PowerSupplyRegPack, PowerSupplyServer, PressureButtonReg, PressureButtonRegPack, PromiseBuffer, PromiseQueue, ProtoTestCmd, ProtoTestCmdPack, ProtoTestEvent, ProtoTestEventPack, ProtoTestPipe, ProtoTestPipePack, ProtoTestReg, ProtoTestRegPack, PulseOximeterReg, PulseOximeterRegPack, READING_SENT, REFRESH, REFRESH_REGISTER_POLL, REGISTER_NODE_NAME, REGISTER_OPTIONAL_POLL_COUNT, REGISTER_POLL_FIRST_REPORT_INTERVAL, REGISTER_POLL_REPORT_INTERVAL, REGISTER_POLL_REPORT_MAX_INTERVAL, REGISTER_POLL_REPORT_VOLATILE_INTERVAL, REGISTER_POLL_REPORT_VOLATILE_MAX_INTERVAL, REGISTER_POLL_STREAMING_INTERVAL, REGISTER_PRE_GET, REGISTER_REFRESH_RETRY_0, REGISTER_REFRESH_RETRY_1, REGISTER_REFRESH_TIMEOUT, REMOVE_LISTENER, RENDER, REPORT_NODE_NAME, REPORT_RECEIVE, REPORT_UPDATE, RESET, RESET_IN_TIME_US, RESTART, ROLE_BOUND, ROLE_CHANGE, ROLE_HAS_NO_SERVICE, ROLE_MANAGER_CHANGE, ROLE_MANAGER_POLL, ROLE_QUERY_DEVICE, ROLE_QUERY_SELF_DEVICE, ROLE_QUERY_SERVICE_INDEX, ROLE_QUERY_SERVICE_OFFSET, ROLE_UNBOUND, RainGaugeReg, RainGaugeRegPack, RainGaugeServer, RandomNumberGeneratorServer, RealTimeClockCmd, RealTimeClockCmdPack, RealTimeClockReg, RealTimeClockRegPack, RealTimeClockServer, RealTimeClockVariant, ReflectedLightReg, ReflectedLightRegPack, ReflectedLightServer, ReflectedLightVariant, RegisterType, RelayReg, RelayRegPack, RelayVariant, RngReg, RngRegPack, RngVariant, RoleManager, RoleManagerClient, RoleManagerCmd, RoleManagerCmdPack, RoleManagerEvent, RoleManagerPipe, RoleManagerPipePack, RoleManagerReg, RoleManagerRegPack, RoleManagerServer, RosCmd, RosCmdPack, RosReportMessage, RosServer, RotaryEncoderReg, RotaryEncoderRegPack, RotaryEncoderServer, RoverReg, RoverRegPack, SELF_ANNOUNCE, SERIAL_TRANSPORT, SERVICE_CLIENT_ADDED, SERVICE_CLIENT_REMOVED, SERVICE_MIXIN_NODE_NAME, SERVICE_NODE_NAME, SERVICE_PROVIDER_ADDED, SERVICE_PROVIDER_REMOVED, SERVICE_TEST_NODE_NAME, SG90_RESPONSE_SPEED, SIDE_DATA, SRV_ACCELEROMETER, SRV_ACIDITY, SRV_AIR_PRESSURE, SRV_AIR_QUALITY_INDEX, SRV_ARCADE_GAMEPAD, SRV_ARCADE_SOUND, SRV_BARCODE_READER, SRV_BIT_RADIO, SRV_BOOTLOADER, SRV_BRAILLE_DISPLAY, SRV_BRIDGE, SRV_BUTTON, SRV_BUZZER, SRV_CAPACITIVE_BUTTON, SRV_CHARACTER_SCREEN, SRV_CLOUD_ADAPTER, SRV_CLOUD_CONFIGURATION, SRV_CODAL_MESSAGE_BUS, SRV_COLOR, SRV_COMPASS, SRV_CONTROL, SRV_DASHBOARD, SRV_DC_CURRENT_MEASUREMENT, SRV_DC_VOLTAGE_MEASUREMENT, SRV_DEVICE_SCRIPT_CONDITION, SRV_DEVICE_SCRIPT_MANAGER, SRV_DEVS_DBG, SRV_DISTANCE, SRV_DMX, SRV_DOT_MATRIX, SRV_DUAL_MOTORS, SRV_E_CO2, SRV_FLEX, SRV_GAMEPAD, SRV_GPIO, SRV_GYROSCOPE, SRV_HEART_RATE, SRV_HID_JOYSTICK, SRV_HID_KEYBOARD, SRV_HID_MOUSE, SRV_HUMIDITY, SRV_I2C, SRV_ILLUMINANCE, SRV_INDEXED_SCREEN, SRV_INFRASTRUCTURE, SRV_KEYBOARD_CLIENT, SRV_LED, SRV_LED_SINGLE, SRV_LED_STRIP, SRV_LIGHT_BULB, SRV_LIGHT_LEVEL, SRV_LOGGER, SRV_MAGNETIC_FIELD_LEVEL, SRV_MAGNETOMETER, SRV_MATRIX_KEYPAD, SRV_MICROPHONE, SRV_MIDI_OUTPUT, SRV_MODEL_RUNNER, SRV_MOTION, SRV_MOTOR, SRV_MULTITOUCH, SRV_PCCONTROLLER, SRV_PCMONITOR, SRV_PLANAR_POSITION, SRV_POTENTIOMETER, SRV_POWER, SRV_POWER_SUPPLY, SRV_PRESSURE_BUTTON, SRV_PROTO_TEST, SRV_PROXY, SRV_PULSE_OXIMETER, SRV_RAIN_GAUGE, SRV_REAL_TIME_CLOCK, SRV_REFLECTED_LIGHT, SRV_RELAY, SRV_RNG, SRV_ROLE_MANAGER, SRV_ROS, SRV_ROTARY_ENCODER, SRV_ROVER, SRV_SAT_NAV, SRV_SENSOR_AGGREGATOR, SRV_SERIAL, SRV_SERVO, SRV_SETTINGS, SRV_SEVEN_SEGMENT_DISPLAY, SRV_SOIL_MOISTURE, SRV_SOLENOID, SRV_SOUND_LEVEL, SRV_SOUND_PLAYER, SRV_SOUND_RECORDER_WITH_PLAYBACK, SRV_SOUND_SPECTRUM, SRV_SPEECH_SYNTHESIS, SRV_SWITCH, SRV_TCP, SRV_TEMPERATURE, SRV_TIMESERIES_AGGREGATOR, SRV_TRAFFIC_LIGHT, SRV_TVOC, SRV_UNIQUE_BRAIN, SRV_USB_BRIDGE, SRV_UV_INDEX, SRV_VERIFIED_TELEMETRY, SRV_VIBRATION_MOTOR, SRV_WATER_LEVEL, SRV_WEIGHT_SCALE, SRV_WIFI, SRV_WIND_DIRECTION, SRV_WIND_SPEED, SRV_WSSK, START, STATE_CHANGE, STOP, STREAMING_DEFAULT_INTERVAL, SUBSCRIBE, SatNavEvent, SatNavReg, SatNavRegPack, SatNavServer, SensorAggregatorReg, SensorAggregatorRegPack, SensorAggregatorSampleType, SensorReg, SensorRegPack, SensorServer, SerialCmd, SerialCmdPack, SerialParityType, SerialReg, SerialRegPack, SerialServer, ServoReg, ServoRegPack, ServoServer, SettingsClient, SettingsCmd, SettingsCmdPack, SettingsEvent, SettingsPipe, SettingsPipePack, SettingsServer, SevenSegmentDisplayCmd, SevenSegmentDisplayCmdPack, SevenSegmentDisplayReg, SevenSegmentDisplayRegPack, SevenSegmentDisplayServer, SoilMoistureReg, SoilMoistureRegPack, SoilMoistureVariant, SolenoidReg, SolenoidRegPack, SolenoidVariant, SoundLevelEvent, SoundLevelReg, SoundLevelRegPack, SoundPlayerCmd, SoundPlayerCmdPack, SoundPlayerPipe, SoundPlayerPipePack, SoundPlayerReg, SoundPlayerRegPack, SoundPlayerServer, SoundRecorderWithPlaybackCmd, SoundRecorderWithPlaybackCmdPack, SoundRecorderWithPlaybackReg, SoundRecorderWithPlaybackRegPack, SoundRecorderWithPlaybackStatus, SoundSpectrumReg, SoundSpectrumRegPack, SpeechSynthesisCmd, SpeechSynthesisCmdPack, SpeechSynthesisReg, SpeechSynthesisRegPack, SpeechSynthesisServer, SwitchEvent, SwitchReg, SwitchRegPack, SwitchServer, SwitchVariant, SystemCmd, SystemCmdPack, SystemEvent, SystemEventPack, SystemReadingThreshold, SystemReg, SystemRegPack, SystemStatusCodes, TIMEOUT, TIMEOUT_DISCONNECT, TRACE, TRACE_FILTER_HORIZON, TRANSPORT_CONNECT_RETRY_DELAY, TRANSPORT_ERROR, TYPESCRIPT_STATIC_NAMESPACE, TcpCmd, TcpCmdPack, TcpPipe, TcpPipeCmd, TcpPipeCmdPack, TcpPipePack, TcpTcpError, TemperatureReg, TemperatureRegPack, TemperatureVariant, TimeseriesAggregatorCmd, TimeseriesAggregatorCmdPack, TimeseriesAggregatorReg, TimeseriesAggregatorRegPack, Trace, TracePlayer, TraceRecorder, TraceView, TrafficLightReg, TrafficLightRegPack, TrafficLightServer, Transport, TvocReg, TvocRegPack, UNBOUND, UPLOAD_BIN, UPLOAD_JSON, USBIO, USB_FILTERS, USB_TRANSPORT, UsbBridgeCmd, UsbBridgeQByte, UvIndexReg, UvIndexRegPack, UvIndexVariant, VIRTUAL_DEVICE_NODE_NAME, VerifiedTelemetryCmd, VerifiedTelemetryEvent, VerifiedTelemetryEventPack, VerifiedTelemetryFingerprintType, VerifiedTelemetryReg, VerifiedTelemetryRegPack, VerifiedTelemetryServer, VerifiedTelemetryStatus, VibrationMotorCmd, VibrationMotorCmdPack, VibrationMotorReg, VibrationMotorRegPack, VibrationMotorServer, WEBSOCKET_TRANSPORT, WallClockScheduler, WaterLevelReg, WaterLevelRegPack, WaterLevelVariant, WebSerialIO, WebSerialTransport, WebSocketBridge, WebSocketTransport, WeightScaleCmd, WeightScaleCmdPack, WeightScaleReg, WeightScaleRegPack, WeightScaleVariant, WifiAPFlags, WifiCmd, WifiCmdPack, WifiEvent, WifiEventPack, WifiPipe, WifiPipePack, WifiReg, WifiRegPack, WifiServer, WindDirectionReg, WindDirectionRegPack, WindSpeedReg, WindSpeedRegPack, WsskCmd, WsskCmdPack, WsskDataType, WsskStreamingType, addComment, addServer, addServiceProvider, addServiceProviderDefinition, anyRandomUint32, arrayConcatMany, arrayEq, arrayShuffle, arrayify, assert, bufferConcat, bufferConcatMany, bufferEq, bufferToArray, bufferToString, cStorage, camelize, capitalize, clampToStorage, cleanStack, clone, commandName, concatBufferArray, converters, crc, createBluetoothTransport, createIFrameBridge, createNodeSPITransport, createNodeSocketTransport, createNodeUSBOptions, createNodeWebSerialTransport, createProxyBridge, createUSBBus, createUSBTransport, createUSBWorkerTransport, createWebBus, createWebSerialTransport, createWebSocketBus, createWebSocketTransport, dashify, dateToClock, debounce, debounceAsync, decodeMember, decodeMembers, decodePacketData, decodeU32LE, delay, dependencyId, deviceCatalog, deviceCatalogImage, deviceServiceName, dualDeviceId, ellipse, ellipseFirstSentence, ellipseJoin, encodeU32LE, encodings, errorCode, escapeDeviceIdentifier, escapeDeviceNameIdentifier, escapeRoleName, flagsToValue, flatClone, fnv1, fromBase64, fromHex, fromUTF8, genFieldInfo, generateDeviceSpecificationId, getBit, getNumber, groupBy, hash, hexDump, hexNum, hsvToCss, humanify, identifierToUrlPath, idiv, inIFrame, injectDevTools, intOfBuffer, isAckError, isActuator, isBufferEmpty, isCancelError, isCodeError, isCommand, isConstRegister, isDeviceId, isDualDeviceId, isEvent, isHighLevelEvent, isHighLevelRegister, isInfrastructure, isInstanceOf, isIntegerType, isIntensity, isLargeFrame, isNumericType, isOptionalReadingRegisterCode, isPipeReport, isPipeReportOf, isReadOnlyRegister, isReading, isRegister, isReportOf, isSensor, isSet, isTimeoutError, isValue, isValueOrIntensity, isWebBluetoothEnabled, isWebBluetoothSupported, isWebSerialEnabled, isWebSerialSupported, isWebSocketTransportSupported, isWebTransportSupported, isWebUSBEnabled, isWebUSBSupported, jdpack, jdpackEqual, jdunpack, jsQuote, jsonCopyFrom, lightEncode, loadServiceSpecifications, localStorageSetting, memberValueToString, memcpy, modifierCodes, nodeSetting, normalizeDeviceSpecification, numberFormatFromStorageType, numberFormatToStorageType, objectToUnpacked, packArguments, packFormat, packInfo, packedValuesIsEqual, packetSpeedTest, parseDeviceId, parseDualDeviceId, parseFirmwareFile, parseIdentifier, parseLogicLog, parsePacketFilter, parseServiceSpecificationMarkdownToJSON, parseTrace, parseUF2Firmware, pick, prettyDuration, prettyEnum, prettyMemberUnit, prettyMicroDuration, prettySize, prettyUnit, printPacket, randomBytes, randomDeviceId, randomRange, randomUInt, range, read16, read32, readBlobToText, readBlobToUint8Array, renderHidMouseButtons, renderKeyboardKey, renderWithPrecision, replayLogicLog, resolveRoleService, resolveUnit, reverseSelectors, rgbToHtmlColor, rgbaToHtmlColor, roundWithPrecision, scaleFloatToInt, scaleIntToFloat, secondaryUnitConverters, selectors, semverCmp, sendStayInBootloaderCommand, sensorSpecifications, serializeToTrace, serviceClass, serviceMap, serviceName, serviceProviderDefinitionFromServiceClass, serviceProviderDefinitions, serviceShortIdOrClass, serviceSpecificationFromClassIdentifier, serviceSpecificationFromName, serviceSpecifications, setBit, setNumber, sevenSegmentDigitEncode, sha256, sha256Hmac, shortDeviceId, signal, sizeOfNumberFormat, snakify, snapshotSensors, splitFilter, stack, startDevTools, startRoles, startServiceProviderFromServiceClass, storageTypeRange, strcmp, stringToBuffer, stringToUint8Array, throttle, throwError, toArray, toAscii, toBase64, toFullHex, toHex, toMap, toUTF8, toggleBit, tonePayload, tryParseMemberValue, uint8ArrayToString, uintOfBuffer, unique, uniqueMap, uniqueName, unitDescription, units, unpackedToObject, updateApplicable, usbRequestDevice, valueToFlags, versionTryParse, watchLocation, wrapComment, wrapDecodedMembers, wrapSnippet, write16, write24, write32 }); //# sourceMappingURL=jacdac.cjs.map