Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature request]: Support Aqara FP1 Regions #13711

Closed
johnsturgeon opened this issue Aug 27, 2022 · 25 comments
Closed

[Feature request]: Support Aqara FP1 Regions #13711

johnsturgeon opened this issue Aug 27, 2022 · 25 comments
Labels
feature request Feature request stale Stale issues

Comments

@johnsturgeon
Copy link

Is your feature request related to a problem? Please describe

Region support in the Aqara FP1 sensor would be great, there are folks who have issues with ceiling fans, and other areas of a monitored room that require ignoring for the sensor to function properly

Describe the solution you'd like

Aqara FP1 device itself supports regions, but that feature is not in zigbee2mqtt, I'd like to be able to define regions, and have region information in the detection event so that I can filter in / out events that I care about.

Describe alternatives you've considered

Positioning the sensor in a way so that it's not seeing the region, but of course, that means there are other regions that I miss.

Additional context

I'm honestly not sure exactly how regions work or are implemented in the FP1, so I'm kind of guessing at what's possible here.

@johnsturgeon johnsturgeon added the feature request Feature request label Aug 27, 2022
@ucsbricks
Copy link

FP1 is a game changer either way, but if we could get regions support, it would make the device perfect.

@ucsbricks
Copy link

deconz-rest-plugin community did a deep dive in FP1 regions:
dresden-elektronik/deconz-rest-plugin#5928

@github-actions
Copy link
Contributor

github-actions bot commented Oct 7, 2022

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days

@github-actions github-actions bot added the stale Stale issues label Oct 7, 2022
@mKeRix
Copy link

mKeRix commented Oct 7, 2022

I’d still be interested!

@github-actions github-actions bot removed the stale Stale issues label Oct 8, 2022
@github-actions
Copy link
Contributor

github-actions bot commented Nov 8, 2022

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days

@github-actions github-actions bot added the stale Stale issues label Nov 8, 2022
@mKeRix
Copy link

mKeRix commented Nov 8, 2022

/unstale

@johnsturgeon
Copy link
Author

johnsturgeon commented Nov 8, 2022

-stale
unstailify

@github-actions github-actions bot removed the stale Stale issues label Nov 9, 2022
@github-actions
Copy link
Contributor

github-actions bot commented Dec 9, 2022

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days

@github-actions github-actions bot added the stale Stale issues label Dec 9, 2022
@patmalcolm91
Copy link

bump

@github-actions github-actions bot removed the stale Stale issues label Dec 10, 2022
@patmalcolm91
Copy link

patmalcolm91 commented Dec 17, 2022

(post edited, since I found a solution to my problem) I managed to get basic region support implemented using the following external converter:

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const ota = require('zigbee-herdsman-converters/lib/ota');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const extend = require('zigbee-herdsman-converters/lib/extend');
const e = exposes.presets;
const ea = exposes.access;

const manufacturerOptions = {
    xiaomi: {manufacturerCode: 0x115f, disableDefaultResponse: true},
};

const octet_as_hex = (octet) => {
    var hex = ""
    for (const num of octet) {
        hex += `:${num.toString(16).padStart(2, 0)}`
    }
    return hex.substring(1);
};

const array_to_byte = (arr) => {
    var byte = 0;
    for (const x of arr) {
        if (x > 4 || x < 0) {
            meta.logger.warn(`zigbee-herdsman-converters:aqara_fp1: Invalid X value ${x}. Ignoring.`);
            continue;
        }
        byte += 2**(x-1)
    }
    return byte;
}


const fzLocal = {
    fp1_regions_events: {
        cluster: 'aqaraOpple',
        type: ['readResponse', 'attributeReport'],
        convert: (model, msg, publish, options, meta) => {
            const event_lookup = {
                1: 'enter',
                2: 'leave',
                4: 'occupied',
                8: 'unoccupied'
            };
            payload = {};
            Object.entries(msg.data).forEach(([key, value]) => {
                switch (parseInt(key)) {
                case 0x0151:
                    const event = event_lookup[value[1]];
                    const region_id = value[0];
                    meta.logger.debug(`zigbee-herdsman-converters:aqara_fp1: event: region ${region_id} ${event}`)
                    payload.region_event = `region_${region_id}_${event}`
                    break;
                case 0xf7:
                    meta.logger.debug(`zigbee-herdsman-converters:aqara_fp1: Unhandled key 0x${key.toString(16).padStart(4, "0")} = ${octet_as_hex(value)}`);
                    break;
                case 0x0142:
                case 0x0143:
                case 0x0144:
                case 0x0146:
                    meta.logger.debug(`zigbee-herdsman-converters:aqara_fp1: Unhandled key 0x${key.toString(16).padStart(4, "0")} = ${value}`);
                    break;
                default:
                    meta.logger.warn(`zigbee-herdsman-converters:aqara_fp1: Unknown key 0x${key.toString(16).padStart(4, "0")} = ${value}`);
                }
            });
            return payload;
        },
    },
};

/*
Config object:
[{"region_id": 1, "action": "create", "definition": {"1": [1, 2, 3, 4]}}]
definition is like {Y1: <array of X values to include>, Y2: ...}, corresponding to X1-X4 and Y1-Y7
*/

const tzLocal = {
    fp1_regions_config: {
        key: ['regions_config'],
        convertSet: async (entity, key, value, meta) => {
            try {
                var val = JSON.parse(value);
            }
            catch(err) {
                meta.logger.debug(`zigbee-herdsman-converters:aqara_fp1: ignoring bad JSON: ${value}`);
            }

            const action_lookup = {create: 1, delete: 2, modify: 3};

            for (var region of val) {
                meta.logger.debug(`zigbee-herdsman-converters:aqara_fp1: trying to ${region['action']} region ${region['region_id']}`);
                action = action_lookup[region['action']];
                var cfg = new Uint8Array(7)
                cfg[6] = (action == 2) ? 0x00 : 0xff; // set least-significant bits based on command
                cfg[0] = action; // set command flag
                cfg[1] = region['region_id']; // set region id
                cfg[2] |= array_to_byte(region['definition']['1'] || [])
                cfg[2] |= array_to_byte(region['definition']['2'] || []) << 4
                cfg[3] |= array_to_byte(region['definition']['3'] || [])
                cfg[3] |= array_to_byte(region['definition']['4'] || []) << 4
                cfg[4] |= array_to_byte(region['definition']['5'] || [])
                cfg[4] |= array_to_byte(region['definition']['6'] || []) << 4
                cfg[5] |= array_to_byte(region['definition']['7'] || [])
                meta.logger.info(`zigbee-herdsman-converters:aqara_fp1: ${region['action']} region ${region['region_id']}: ${octet_as_hex(cfg)}`);
                await entity.write('aqaraOpple', {0x0150: {value: cfg, type: 0x41}}, manufacturerOptions.xiaomi);
            }
        },
    },
    fp1_other_regions_config: {
        key: ['exits_entrances', 'interference_sources', 'edges'],
        convertSet: async (entity, key, value, meta) => {
            try {
                var val = JSON.parse(value);
            }
            catch(err) {
                meta.logger.debug(`zigbee-herdsman-converters:aqara_fp1: ignoring bad JSON: ${value}`);
                return;
            }
            meta.logger.debug(`zigbee-herdsman-converters:aqara_fp1: trying to update ${key}`);
            var cfg = 0;
            for (y in val) {
                cfg |= array_to_byte(val[y] || []) << (4*(Number(y)-1));
            }
            meta.logger.info(`zigbee-herdsman-converters:aqara_fp1: setting ${key} = 0x${cfg.toString(16).padStart(8, '0')}`);
            switch (key) {
                case 'exits_entrances':
                    await entity.write('aqaraOpple', {0x0153: {value: cfg, type: 0x23}}, manufacturerOptions.xiaomi);
                    break;
                case 'interference_sources':
                    await entity.write('aqaraOpple', {0x0154: {value: cfg, type: 0x23}}, manufacturerOptions.xiaomi);
                    break;
                case 'edges':
                    await entity.write('aqaraOpple', {0x0156: {value: cfg, type: 0x23}}, manufacturerOptions.xiaomi);
                    break;
            }
        }
    }
};

const definition = {
    zigbeeModel: ['lumi.motion.ac01'],
    model: 'RTCZCGQ11LM',
    vendor: 'Xiaomi',
    description: 'Aqara presence detector FP1 (experimental region support)',
    fromZigbee: [fzLocal.fp1_regions_events, fz.aqara_opple],
    toZigbee: [tz.RTCZCGQ11LM_presence, tz.RTCZCGQ11LM_monitoring_mode, tz.RTCZCGQ11LM_approach_distance,
        tz.aqara_motion_sensitivity, tz.RTCZCGQ11LM_reset_nopresence_status, tzLocal.fp1_regions_config, tzLocal.fp1_other_regions_config],
    exposes: [e.presence().withAccess(ea.STATE_GET),
        exposes.enum('presence_event', ea.STATE, ['enter', 'leave', 'left_enter', 'right_leave', 'right_enter', 'left_leave',
            'approach', 'away']).withDescription('Presence events: "enter", "leave", "left_enter", "right_leave", ' +
            '"right_enter", "left_leave", "approach", "away"'),
        exposes.enum('monitoring_mode', ea.ALL, ['undirected', 'left_right']).withDescription('Monitoring mode with or ' +
            'without considering right and left sides'),
        exposes.enum('approach_distance', ea.ALL, ['far', 'medium', 'near']).withDescription('The distance at which the ' +
            'sensor detects approaching'),
        exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high']).withDescription('Different sensitivities ' +
            'means different static human body recognition rate and response speed of occupied'),
        exposes.enum('reset_nopresence_status', ea.SET, ['']).withDescription('Reset the status of no presence'),
        e.device_temperature(), e.power_outage_count(), exposes.text('regions_config', ea.SET),
        exposes.enum('region_event', ea.STATE, ['region_*_enter', 'region_*_leave', 'region_*_occupied', 'region_*_unoccupied']).withDescription('Region events'),
        exposes.text('exits_entrances', ea.SET), exposes.text('interference_sources', ea.SET), exposes.text('edges', ea.SET),
    ],
    configure: async (device, coordinatorEndpoint, logger) => {
        const endpoint = device.getEndpoint(1);
        await endpoint.read('aqaraOpple', [0x010c], {manufacturerCode: 0x115f});
        await endpoint.read('aqaraOpple', [0x0142], {manufacturerCode: 0x115f});
        await endpoint.read('aqaraOpple', [0x0144], {manufacturerCode: 0x115f});
        await endpoint.read('aqaraOpple', [0x0146], {manufacturerCode: 0x115f});
    },
    ota: ota.zigbeeOTA,
};

module.exports = definition;

In order to add a region, you need to pass a special JSON configuration object to the regions_config attribute. This should look like this:

[{"region_id": 1, "action": "create", "definition": {"1": [1, 2, 3, 4], "2": [2, 3]}}]

Where the definition value is an object with keys corresponding to the Y coordinates (1-7), and the values are arrays of corresponding X coordinates (1-4). See dresden-elektronik/deconz-rest-plugin#5928 (comment) for more detail. Once you have set up at least one region, events will start to be reported on the attribute region_event.

EDIT 2: support for entrance/exit, interference source, and edge regions is now included. These are set by setting the corresponding attribute to a JSON object of the same format as the "definition" object above.

There's still quite a bit of work that could be done on the UI/UX. Suggestions / improvements are welcome.

@maxi1134
Copy link

maxi1134 commented Jan 4, 2023

Any chance of seeing this included in the next Z2M version?

@patmalcolm91
Copy link

Any chance of seeing this included in the next Z2M version?

Unfortunately I don't have much time currently to finalize the features and prepare a pull request and won't for a few more weeks. Also, one major thing that still should be changed before inclusion in a release is to make the region configuration part of the device-specific settings rather than the normal "exposes" tab. After looking through many examples, I still can't figure out how to do this. If anyone has any tips on that, let me know.

@mdziekon
Copy link

mdziekon commented Jan 7, 2023

@patmalcolm91 are you OK with me taking your code and submitting a PR based on your solution (with some code refactoring), mostly as is? I was not able to figure out how to move the newly added configs to "Device specific settings" as well, but the rest works just fine, so I should be able to prepare a merge for basic support.

@patmalcolm91
Copy link

@patmalcolm91 are you OK with me taking your code and submitting a PR based on your solution (with some code refactoring), mostly as is? I was not able to figure out how to move the newly added configs to "Device specific settings" as well, but the rest works just fine, so I should be able to prepare a merge for basic support.

Yes, feel free. That would be great!

mdziekon added a commit to mdziekon/zigbee-herdsman-converters that referenced this issue Jan 8, 2023
mdziekon added a commit to mdziekon/zigbee-herdsman-converters that referenced this issue Jan 8, 2023
mdziekon added a commit to mdziekon/zigbee-herdsman-converters that referenced this issue Jan 9, 2023
mdziekon added a commit to mdziekon/zigbee-herdsman-converters that referenced this issue Jan 9, 2023
mdziekon added a commit to mdziekon/zigbee-herdsman-converters that referenced this issue Jan 9, 2023
mdziekon added a commit to mdziekon/zigbee-herdsman-converters that referenced this issue Jan 9, 2023
mdziekon added a commit to mdziekon/zigbee-herdsman-converters that referenced this issue Jan 17, 2023
mdziekon added a commit to mdziekon/zigbee-herdsman-converters that referenced this issue Jan 17, 2023
mdziekon added a commit to mdziekon/zigbee-herdsman-converters that referenced this issue Jan 18, 2023
mdziekon added a commit to mdziekon/zigbee-herdsman-converters that referenced this issue Jan 18, 2023
Koenkk added a commit to Koenkk/zigbee-herdsman-converters that referenced this issue Jan 18, 2023
* Implement Aqara FP1 region events handling

Koenkk/zigbee2mqtt#13711

* Improve region_event field description

* Implement Aqara FP1 regions configuration

Koenkk/zigbee2mqtt#13711

* Move xiaomi Aqara FP1 definitions & mappers to "constants.js" file

* Flatten input parser for regions config & improve error reporting

* Improve regions_config field description

* Cleanups

* Refactor upserting & deleting regions to use exposes.composite instead of JSON as input

* Move constants & mappers to xiaomi-specific file

* Change region_event into action to leverage HA-integration features

* Update xiaomi.js

* Update utils.js

* Update constants.js

* Update xiaomi.js

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
@laplacacm
Copy link

This is exciting! It looks like it is getting close. Thank you to all that helped with this!

@chrigu1981
Copy link

Nice to see some progress on this topic. But, it does not work...Setting a region works after some attempts but when i reload the page, the settings are gone. Somehow not saved....

@mdziekon
Copy link

mdziekon commented Feb 2, 2023

@chrigu1981

Nice to see some progress on this topic. But, it does not work...Setting a region works after some attempts but when i reload the page, the settings are gone. Somehow not saved....

That does not necessarily mean the settings were not saved. Have you tried to actually trigger a region event? To verify whether the setting upload (to the device) was successful, you can try to trigger a region event, and verify if it's present in the logs or in momentary action field.

I'm unaware of any option to retrieve these settings from the device, so settings are uploaded to the device, but not persisted anywhere in Z2M. Maybe there's a way to persist this in Z2M's state as a secondary settings storage, but I'm not that proficient with Z2M's architecture to know that.

@chrigu1981
Copy link

chrigu1981 commented Feb 2, 2023

@mdziekon thx for your answer.

i tried setting regions over MQTT as well in the z2m UI and also tried to manually add them in the state.json file, non of them seems to work.

Manually adding it in the state.json file:

"0x1234567890": {
        "device_temperature": 26,
        "power_outage_count": 4,
        "presence": true,
        "motion_sensitivity": "high",
        "monitoring_mode": "undirected",
        "approach_distance": "far",
        "presence_event": "away",
        "region_upsert": {
            "region_id":1,
            "zones": [
                {"x":1,"y":1}
            ]
        },
        "last_seen": 1675354754264,
        "linkquality": 43,
        "update": {
            "state": "idle",
            "installed_version": 54,
            "latest_version": 54
        },
        "update_available": false
    }

Btw: I walked around with my Macbook but never got an action :)

Log

Debug 2023-02-02 18:47:40
Received MQTT message on 'zigbee2mqtt/0x1234567890/set' with data '{ "region_upsert": { "region_id": 2, "zones": [ {"x":1,"y":1} ] } }'

Debug 2023-02-02 18:47:40
Publishing 'set' 'region_upsert' to '0x1234567890'

Debug 2023-02-02 18:47:40
zigbee-herdsman-converters:xiaomi:aqara_fp1:region_upsert: trying to create region 2

Info 2023-02-02 18:47:40
zigbee-herdsman-converters:xiaomi:aqara_fp1:region_upsert: create region 2 01:02:01:00:00:00:ff

Info 2023-02-02 18:47:40
MQTT publish: topic 'zigbee2mqtt/0x1234567890', payload '{"approach_distance":"far","device_temperature":26,"last_seen":1675360060856,"linkquality":72,"monitoring_mode":"undirected","motion_sensitivity":"high","power_outage_count":4,"presence":true,"presence_event":"away","update":{"installed_version":54,"latest_version":54,"state":"idle"},"update_available":false}'

@TzachiGuetta
Copy link

I have this in the homebridge log:
[02/02/2023, 19:40:34] [zigbee2mqtt] Failed to setup stateless programmable switch for accessory Aqara FP1 from expose "{"access":1,"description":"Most recent region event. Event template is "region_<REGION_ID><EVENT_TYPE>", where <REGION_ID> is region number (1-10), <EVENT_TYPE> is one of "enter", "leave", "occupied", "unoccupied". "enter" / "leave" events are usually triggered first, followed by "occupied" / "unoccupied" after a couple of seconds.","name":"action","property":"action","type":"enum","values":["regionenter","regionleave","regionoccupied","regionunoccupied"]}", error: Error: Device found with a wildcard in the exposed possible values for the action, which cannot be mapped: region*_enter

what does it means?

@mdziekon
Copy link

mdziekon commented Feb 2, 2023

@chrigu1981

Setting it in state.json directly won't work, for the reason mentioned above. You either have to use the GUI, or issue a command in device's dev console manually.

I've just tried out the new update (as I usually wait a couple of days for the rest of the stuff to stabilise), tested it on two FP1s I have currently wired up (one in the kitchen, one in the bathroom).

I was able to add a region (y = 1, x = 1,2,3,4, so quite a wide area of detection) at my kitchen's sensor, and after that it properly detects the region events:

debug 2023-02-02 19:07:51 zigbee-herdsman-converters:xiaomi:aqara_fp1: action: Triggered event (region "1", type "enter")
debug 2023-02-02 19:07:53 zigbee-herdsman-converters:xiaomi:aqara_fp1: action: Triggered event (region "1", type "leave")
debug 2023-02-02 19:08:01 zigbee-herdsman-converters:xiaomi:aqara_fp1: Unhandled key 0x0143 = 7
debug 2023-02-02 19:08:07 zigbee-herdsman-converters:xiaomi:aqara_fp1: Unhandled key 0x0143 = 6
debug 2023-02-02 19:08:10 zigbee-herdsman-converters:xiaomi:aqara_fp1: action: Triggered event (region "1", type "enter")
debug 2023-02-02 19:08:13 zigbee-herdsman-converters:xiaomi:aqara_fp1: action: Triggered event (region "1", type "occupied")
debug 2023-02-02 19:08:35 zigbee-herdsman-converters:xiaomi:aqara_fp1: action: Triggered event (region "1", type "leave")
debug 2023-02-02 19:08:42 zigbee-herdsman-converters:xiaomi:aqara_fp1: Unhandled key 0x0143 = 7
debug 2023-02-02 19:08:45 zigbee-herdsman-converters:xiaomi:aqara_fp1: action: Triggered event (region "1", type "unoccupied")

I was also able to trigger a simpe mobile notification using this HomeAssistant automation:

alias: test
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.kitchen_presence_sensor_action
    to: region_1_enter
condition: []
action:
  - service: notify.grouped_bygroup_admin_mobile
    data:
      message: "Triggered: Kitchen, region 1, event enter"
mode: single

Screenshot_20230202-191407


However, for some weird reason, my other sensor (in the bathroom) doesn't pick up the update, and after some time there's an error log with a timeout from that device. However, it's not the first time I'm seeing this particular device acting up, it's either broken or I have to pair it again to fix its behavior.

@chrigu1981
Copy link

@mdziekon i have 3 FP1‘s but not 1 is working. Are you using the Sonoff Zigbee Dongle (2652P)? With which firmware?

@mdziekon
Copy link

mdziekon commented Feb 2, 2023

@chrigu1981 no, I'm using conbee II, with the latest firmware, and all my FP1s have been updated to the latest firmware version.

@chrigu1981
Copy link

chrigu1981 commented Feb 2, 2023

@mdziekon Just to clarify, all are working but not with regions. Not sure if i have the latest FW on the sensors but i will try to update them….

Anyway many thanks for your help!

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2023

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days

@github-actions github-actions bot added the stale Stale issues label Mar 5, 2023
@johnsturgeon
Copy link
Author

I'm closing this issue since it's merged, any future issues with regions should probably just be their own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Feature request stale Stale issues
Projects
None yet
Development

No branches or pull requests

9 participants