diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 88bf616..1f0e04b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,8 +4,8 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "monthly" - package-ecosystem: "npm" directory: "/" schedule: - interval: "weekly" + interval: "monthly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a883cf..094354d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [12.x] + node-version: [18.x] steps: - name: Check out the repo @@ -53,9 +53,9 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Push to DockerHub - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: platforms: linux/amd64,linux/arm64,linux/arm/v7 push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.docker_meta.outputs.tags }} - labels: ${{ steps.docker_meta.outputs.labels }} \ No newline at end of file + labels: ${{ steps.docker_meta.outputs.labels }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0d645a..2968f1a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,9 +32,9 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Push to DockerHub - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: platforms: linux/amd64,linux/arm64,linux/arm/v7 push: true tags: ${{ steps.docker_meta.outputs.tags }} - labels: ${{ steps.docker_meta.outputs.labels }} \ No newline at end of file + labels: ${{ steps.docker_meta.outputs.labels }} diff --git a/.nvmrc b/.nvmrc index e1fcd1e..c32828c 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -lts/erbium +lts/hydrogen \ No newline at end of file diff --git a/HA-MQTT.md b/HA-MQTT.md index 05fe7b3..e6a3b0b 100644 --- a/HA-MQTT.md +++ b/HA-MQTT.md @@ -1,5 +1,42 @@ Sample configs for MQTT Home Assistant integration. +### Commands + +#### example script yaml: +```yaml +alias: Car - Start Vehicle +sequence: + - service: mqtt.publish + data: + topic: homeassistant/YOUR_CAR_VIN/command + payload: '{"command": "startVehicle"}' +mode: single +icon: 'mdi:car-electric' +``` + +#### Triger precondition via calendar +````yaml +alias: Car Precondition +description: Precondition if group.family is home (ie, at least one person). +trigger: + - platform: state + entity_id: calendar.YOUR_CAL_NAME + from: 'off' + to: 'on' +condition: + - condition: state + entity_id: group.family + state: home + - condition: state + entity_id: calendar.YOUR_CAL_NAME + state: Bolt Start + attribute: message +action: + - service: script.car_start_vehicle + data: {} +mode: single +```` + ### Location Unfortunately, the MQTT Device tracker uses a home/not_home state and the MQTT Json device tracker does not support the discovery schema so a manual entity configuration is required. @@ -23,23 +60,21 @@ sequence: mode: single icon: 'mdi:map-marker' ``` - -### Lovelace Dashboard -Create a new dashboard, or use the cards in your own view. The `mdi:car-electric` icon works well here. - -![lovelace screenshot](images/lovelace.png) - -#### script yaml: -```yaml -alias: Car - Start Vehicle -sequence: - - service: mqtt.publish - data: - topic: homeassistant/YOUR_CAR_VIN/command - payload: '{"command": "startVehicle"}' +### Automation: +Create an automation to update the location whenever the odometer changes, instead of on a time interval. +```alias: Update EV Location +description: "" +trigger: + - platform: state + entity_id: + - sensor.odometer_mi +condition: [] +action: + - service: script.locate_bolt_ev + data: {} mode: single -icon: 'mdi:car-electric' ``` + #### Commands: [OnStarJS Command Docs](https://github.com/samrum/OnStarJS#commands) 1. `getAccountVehicles` @@ -53,6 +88,12 @@ icon: 'mdi:car-electric' 9. `cancelChargeOverride` 10. `getLocation` + +### Lovelace Dashboard +Create a new dashboard, or use the cards in your own view. The `mdi:car-electric` icon works well here. + +![lovelace screenshot](images/lovelace.png) + #### dashboard yaml: ```yaml views: diff --git a/README.md b/README.md index 4508d43..37ca614 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ Collect the following information: 4. MQTT server information: hostname, username, password 4a. If using TLS, define `MQTT_PORT` and `MQTT_TLS=true` - ### Node.js It's a typical node.js application, that uses .env variables to run. To install and run, follow the steps bellow. ``` diff --git a/package-lock.json b/package-lock.json index 49862c5..2d0345e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11445,4 +11445,4 @@ "dev": true } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 4d4b6c3..8b3a007 100644 --- a/package.json +++ b/package.json @@ -49,4 +49,4 @@ "mocha": "^10.2.0", "nyc": "^15.1.0" } -} +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index 470b9fa..0081243 100644 --- a/src/index.js +++ b/src/index.js @@ -26,7 +26,9 @@ const onstarConfig = { onStarPin: process.env.ONSTAR_PIN || ospin, checkRequestStatus: process.env.ONSTAR_SYNC === "true" || true, refreshInterval: parseInt(process.env.ONSTAR_REFRESH) || (30 * 60 * 1000), // 30 min - allowCommands: _.toLower(_.get(process, 'env.ONSTAR_ALLOW_COMMANDS', 'true')) === 'true' + requestPollingIntervalSeconds: parseInt(process.env.ONSTAR_POLL_INTERVAL) || 6, // 6 sec default + requestPollingTimeoutSeconds: parseInt(process.env.ONSTAR_POLL_TIMEOUT) || 60, // 60 sec default + allowCommands: _.get(process.env, 'ONSTAR_ALLOW_COMMANDS', 'true') === 'true' }; logger.info('OnStar Config', {onstarConfig}); @@ -90,20 +92,23 @@ const configureMQTT = async (commands, client, mqttHA) => { return; } const commandFn = cmd.bind(commands); - logger.info('Command sent', {command}); + logger.info('Command sent', { command }); commandFn(options || {}) .then(data => { // TODO refactor the response handling for commands - logger.info('Command completed', {command}); - const location = _.get(data, 'response.data.commandResponse.body.location'); - if (data && location) { - logger.info('Command response data', {location}); - const topic = mqttHA.getStateTopic({name: command}); - // TODO create device_tracker entity. MQTT device tracker doesn't support lat/lon and mqtt_json - // doesn't have discovery - client.publish(topic, - JSON.stringify({latitude: location.lat, longitude: location.long}), {retain: true}) - .then(() => logger.info('Published location to topic.', {topic})); + logger.info('Command completed', { command }); + const responseData = _.get(data, 'response.data'); + if (responseData) { + logger.info('Command response data', { responseData }); + const location = _.get(data, 'response.data.commandResponse.body.location'); + if (location) { + const topic = mqttHA.getStateTopic({ name: command }); + // TODO create device_tracker entity. MQTT device tracker doesn't support lat/lon and mqtt_json + // doesn't have discovery + client.publish(topic, + JSON.stringify({ latitude: location.lat, longitude: location.long }), { retain: true }) + .then(() => logger.info('Published location to topic.', { topic })); + } } }) .catch(err=> logger.error('Command error', {command, err})); @@ -178,7 +183,17 @@ const configureMQTT = async (commands, client, mqttHA) => { const main = async () => run() .then(() => logger.info('Updates complete, sleeping.')) - .catch(e => logger.error('Error', {error: e})) + .catch(e => { + if (e instanceof Error) { + logger.error('Error', {error: _.pick(e, [ + 'message', 'stack', + 'response.status', 'response.statusText', 'response.headers', 'response.data', + 'request.method', 'request.body', 'request.contentType', 'request.headers', 'request.url' + ])}); + } else { + logger.error('Error', {error: e}); + } + }); await main(); setInterval(main, onstarConfig.refreshInterval);